Minimales lauffähiges Beispiel
GitHub Upstream mit QEMU + Buildroot Boilerplate:
- poll.ko-Kernelmodul
- poll.out Userland-Test
In diesem vereinfachten Beispiel generieren wir Umfrageereignisse aus einem separaten Thread. Im wirklichen Leben werden Poll-Ereignisse wahrscheinlich durch Interrupts ausgelöst, wenn die Hardware einen Job beendet hat und neue Daten für das Userland zum Lesen verfügbar wurden.
Der wichtigste Punkt, an den Sie sich erinnern sollten, ist, dass wenn poll
Null zurückgibt, ruft der Kernel erneut auf:Warum müssen wir poll_wait in poll aufrufen?
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 Userland:
#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);
}
}
}
Verwendung:
insmod poll.ko
mount -t debugfs none /sys/kernel/debug
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll
Ergebnis:jiffies
wird jede Sekunde aus dem Userland auf stdout ausgegeben, z. B.:
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
Erzwingen Sie die Umfrage file_operation
um 0 zurückzugeben, um klarer zu sehen, was passiert:
insmod poll.ko ret0=1
Beispielausgabe:
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
Daraus sehen wir, dass die Kontrolle nicht an das Userland zurückgegeben wird:Der Kernel ruft einfach weiterhin die Abfrage file_operation
auf immer wieder.
Getestet auf Linux 5.4.3.
Sie können einige gute Beispiele im Kernel selbst finden. Sehen Sie sich die nächsten Dateien an:
- drivers/rtc/dev.c, drivers/rtc/interface.c
- kernel/printk/printk.c
- drivers/char/random.c
Um poll()
hinzuzufügen Funktion zu Ihrem Code folgen Sie den nächsten Schritten.
-
Erforderliche Header einschließen:
#include <linux/wait.h> #include <linux/poll.h>
-
Warteschlangenvariable deklarieren:
static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
-
Fügen Sie
fortune_poll()
hinzu Funktion und fügen Sie sie hinzu (als.poll
Callback) zu Ihrer Dateioperationsstruktur: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, };
Beachten Sie, dass Sie
POLLIN | POLLRDNORM
zurückgeben sollten wenn Sie neue Daten zum Lesen haben, und0
falls es keine neuen Daten zum Lesen gibt (poll()
Anruf abgelaufen). Weitere Informationen finden Sie in der Umfrage von Mann 2. -
Benachrichtigen Sie Ihre Warteschlange, sobald Sie neue Daten haben:
wake_up_interruptible(&fortune_wait);
Das ist das Grundlegende zur Implementierung von poll()
Betrieb. Abhängig von Ihrer Aufgabe müssen Sie möglicherweise eine Warteschlangen-API in Ihrem .read
verwenden Funktion (wie wait_event_interruptible()
).
Siehe auch verwandte Frage:Implementing poll in a Linux kernel module.