Contoh minimal yang dapat dijalankan
GitHub upstream dengan QEMU + Buildroot boilerplate:
- modul kernel poll.ko
- pengujian wilayah pengguna poll.out
Dalam contoh yang disederhanakan ini, kami membuat acara polling dari utas terpisah. Dalam kehidupan nyata, peristiwa jajak pendapat kemungkinan akan dipicu oleh interupsi, saat perangkat keras menyelesaikan beberapa pekerjaan, dan data baru tersedia untuk dibaca oleh pengguna.
Hal utama yang harus diingat adalah jika poll
mengembalikan nol, kernel memanggilnya lagi:Mengapa kita perlu memanggil poll_wait di polling?
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);
}
}
}
Penggunaan:
insmod poll.ko
mount -t debugfs none /sys/kernel/debug
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll
Hasil:jiffies
dicetak ke stdout setiap detik dari userland, misalnya:
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
Paksa polling file_operation
untuk mengembalikan 0 untuk melihat apa yang terjadi dengan lebih jelas:
insmod poll.ko ret0=1
Keluaran sampel:
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
Dari sini kita melihat bahwa kontrol tidak dikembalikan ke userland:kernel terus memanggil polling file_operation
lagi dan lagi.
Diuji pada Linux 5.4.3.
Anda dapat menemukan beberapa contoh bagus di kernel itu sendiri. Lihat file berikutnya:
- driver/rtc/dev.c, driver/rtc/interface.c
- kernel/printk/printk.c
- drivers/char/random.c
Untuk menambahkan poll()
fungsi ke kode Anda, ikuti langkah selanjutnya.
-
Sertakan tajuk yang diperlukan:
#include <linux/wait.h> #include <linux/poll.h>
-
Deklarasikan variabel antrian tunggu:
static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
-
Tambahkan
fortune_poll()
function dan menambahkannya (sebagai.poll
callback) ke struktur operasi file Anda: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, };
Perhatikan bahwa Anda harus mengembalikan
POLLIN | POLLRDNORM
jika Anda memiliki beberapa data baru untuk dibaca, dan0
seandainya tidak ada data baru untuk dibaca (poll()
batas waktu panggilan). Lihat jajak pendapat pria 2 untuk detailnya. -
Beri tahu antrian tunggu Anda setelah Anda memiliki data baru:
wake_up_interruptible(&fortune_wait);
Itulah hal dasar tentang mengimplementasikan poll()
operasi. Bergantung pada tugas Anda, Anda mungkin perlu menggunakan beberapa API antrian tunggu di .read
Anda fungsi (seperti wait_event_interruptible()
).
Lihat juga pertanyaan terkait:Mengimplementasikan polling dalam modul kernel Linux.