最小限の実行可能な例
QEMU + Buildroot ボイラープレートを使用した GitHub アップストリーム:
- poll.ko カーネル モジュール
- poll.out ユーザーランド テスト
この簡単な例では、別のスレッドからポーリング イベントを生成します。実際には、ポーリング イベントは、ハードウェアが何らかのジョブを終了し、新しいデータがユーザーランドで読み取れるようになったときに、割り込みによってトリガーされる可能性があります。
覚えておくべき主なポイントは、 poll
の場合 ゼロを返すと、カーネルはそれを再度呼び出します:poll で poll_wait を呼び出す必要があるのはなぜですか?
poll.ko
#include <linux/debugfs.h>
#include <linux/delay.h> /* usleep_range */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> /* min */
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/printk.h> /* printk */
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible */
#include <uapi/linux/stat.h> /* S_IRUSR */
static int ret0 = 0;
module_param(ret0, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(i, "if 1, always return 0 from poll");
static char readbuf[1024];
static size_t readbuflen;
static struct dentry *debugfs_file;
static struct task_struct *kthread;
static wait_queue_head_t waitqueue;
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
if (copy_to_user(buf, readbuf, readbuflen)) {
ret = -EFAULT;
} else {
ret = readbuflen;
}
/* This is normal pipe behaviour: data gets drained once a reader reads from it. */
/* https://stackoverflow.com/questions/1634580/named-pipes-fifos-on-unix-with-multiple-readers */
readbuflen = 0;
return ret;
}
/* If you return 0 here, then the kernel will sleep until an event
* happens in the queue. and then call this again, because of the call to poll_wait. */
unsigned int poll(struct file *filp, struct poll_table_struct *wait)
{
pr_info("poll\n");
/* This doesn't sleep. It just makes the kernel call poll again if we return 0. */
poll_wait(filp, &waitqueue, wait);
if (readbuflen && !ret0) {
pr_info("return POLLIN\n");
return POLLIN;
} else {
pr_info("return 0\n");
return 0;
}
}
static int kthread_func(void *data)
{
while (!kthread_should_stop()) {
readbuflen = snprintf(
readbuf,
sizeof(readbuf),
"%llu",
(unsigned long long)jiffies
);
usleep_range(1000000, 1000001);
pr_info("wake_up\n");
wake_up(&waitqueue);
}
return 0;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = read,
.poll = poll
};
static int myinit(void)
{
debugfs_file = debugfs_create_file(
"lkmc_poll", S_IRUSR | S_IWUSR, NULL, NULL, &fops);
init_waitqueue_head(&waitqueue);
kthread = kthread_create(kthread_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
static void myexit(void)
{
kthread_stop(kthread);
debugfs_remove(debugfs_file);
}
module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
poll.out ユーザーランド:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */
int main(int argc, char **argv) {
char buf[1024];
int fd, i, n;
short revents;
struct pollfd pfd;
if (argc < 2) {
fprintf(stderr, "usage: %s <poll-device>\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
pfd.fd = fd;
pfd.events = POLLIN;
while (1) {
puts("poll");
i = poll(&pfd, 1, -1);
if (i == -1) {
perror("poll");
assert(0);
}
revents = pfd.revents;
printf("revents = %d\n", revents);
if (revents & POLLIN) {
n = read(pfd.fd, buf, sizeof(buf));
printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
}
}
}
使い方:
insmod poll.ko
mount -t debugfs none /sys/kernel/debug
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll
結果:jiffies
ユーザーランドから毎秒 stdout に出力されます。例:
poll
<6>[ 4.275305] poll
<6>[ 4.275580] return POLLIN
revents = 1
POLLIN n=10 buf=4294893337
poll
<6>[ 4.276627] poll
<6>[ 4.276911] return 0
<6>[ 5.271193] wake_up
<6>[ 5.272326] poll
<6>[ 5.273207] return POLLIN
revents = 1
POLLIN n=10 buf=4294893588
poll
<6>[ 5.276367] poll
<6>[ 5.276618] return 0
<6>[ 6.275178] wake_up
<6>[ 6.276370] poll
<6>[ 6.277269] return POLLIN
revents = 1
POLLIN n=10 buf=4294893839
ポーリング file_operation
を強制する 何が起こるかをより明確に見るために 0 を返す:
insmod poll.ko ret0=1
出力例:
poll
<6>[ 85.674801] poll
<6>[ 85.675788] return 0
<6>[ 86.675182] wake_up
<6>[ 86.676431] poll
<6>[ 86.677373] return 0
<6>[ 87.679198] wake_up
<6>[ 87.680515] poll
<6>[ 87.681564] return 0
<6>[ 88.683198] wake_up
このことから、制御がユーザーランドに戻されていないことがわかります。カーネルはポーリング file_operation
を呼び出し続けます。 何度も何度も。
Linux 5.4.3 でテスト済み。
カーネル自体にいくつかの良い例があります。次のファイルを見てください:
- drivers/rtc/dev.c、drivers/rtc/interface.c
- kernel/printk/printk.c
- drivers/char/random.c
poll()
を追加するには 次の手順に従います。
必要なヘッダーを含めます:
#include <linux/wait.h>
#include <linux/poll.h>
waitqueue 変数を宣言します:
static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
fortune_poll()
を追加 関数を追加して (.poll
として) callback) をファイル操作構造に:
static unsigned int fortune_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &fortune_wait, wait);
if (new-data-is-ready)
return POLLIN | POLLRDNORM;
return 0;
}
static const struct file_operations proc_test_fops = {
....
.poll = fortune_poll,
};
POLLIN | POLLRDNORM
を返す必要があることに注意してください 読み取る新しいデータがある場合、および 0
読み取る新しいデータがない場合 (poll()
コールタイムアウト)。詳細については、man 2 の投票を参照してください。
新しいデータを取得したら、waitqueue に通知します:
wake_up_interruptible(&fortune_wait);
これが poll()
の実装に関する基本的なことです 手術。タスクによっては、.read
でいくつかの waitqueue API を使用する必要がある場合があります。 関数 (wait_event_interruptible()
など) ).
関連する質問:Linux カーネル モジュールでのポーリングの実装も参照してください。