如何将轮询函数添加到内核模块代码?

据我所知,从内核空间通知用户空间,一种方法是使用民意调查。 这意味着内核驱动程序应该首先提供poll方法。 下面的代码是从互联网上find的,它真的有效!

#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/string.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Fortune Cookie Kernel Module"); MODULE_AUTHOR("M. Tim Jones"); #define MAX_COOKIE_LENGTH PAGE_SIZE static struct proc_dir_entry *proc_entry; static char *cookie_buf; // Space for fortune strings static int write_index; // Index to write next fortune static int read_index; // Index to read next fortune ssize_t fortune_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) // Refer to: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); { int space_available = (MAX_COOKIE_LENGTH-write_index); if (len > space_available) { printk(KERN_INFO "fortune: cookie buffer is full!\n"); return -ENOSPC; } if (copy_from_user( &cookie_buf[write_index], buff, len )) { return -EFAULT; } write_index += len; cookie_buf[write_index-1] = 0; return len; } ssize_t fortune_read(struct file *file, char *buf, size_t count, loff_t *f_pos){ // Refer to: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); int len; //there's no fortune or a fortune has already been read //the *f_pos > 0 hack is needed because `cat /proc/fortune` would otherwise //display every thing in the cookie_buf if(write_index == 0 || *f_pos > 0){ return 0; } // cicle through fortunes if(read_index >= write_index){ read_index = 0; } len = sprintf(buf, "%s\n", &cookie_buf[read_index]); read_index += len; *f_pos += len; return len; } static const struct file_operations proc_test_fops = { .owner = THIS_MODULE, // .open = led_proc_open, .read = fortune_read, // .llseek = seq_lseek, // .release = single_release, .write = fortune_write, // unsigned int (*poll) (struct file *, struct poll_table_struct *); // int (*fasync) (int, struct file *, int); }; int __init init_fortune_module( void ) { int ret = 0; cookie_buf = (char *)vmalloc( MAX_COOKIE_LENGTH ); if (!cookie_buf) { ret = -ENOMEM; } else { memset( cookie_buf, 0, MAX_COOKIE_LENGTH ); // proc_entry = create_proc_entry( "fortune", 0644, NULL ); proc_entry = proc_create( "fortune", 0644, NULL, &proc_test_fops ); if (proc_entry == NULL) { ret = -ENOMEM; vfree(cookie_buf); printk(KERN_INFO "fortune: Couldn't create proc entry\n"); } else { write_index = 0; read_index = 0; printk(KERN_INFO "fortune: Module loaded.\n"); } } return ret; } void __exit exit_fortune_module( void ) { // remove_proc_entry("fortune", &proc_entry); proc_remove(proc_entry); vfree(cookie_buf); printk(KERN_INFO "fortune: Module unloaded.\n"); } module_init( init_fortune_module ); module_exit( exit_fortune_module ); 

我可以这样做,使其工作:

 echo "hello" > /proc/fortune 

接着

 cat /proc/fortune 

看到结果。

但是如何添加poll方法呢? 我尝试了一些,但仍然失败。 谁能帮忙? 谢谢!

你可以在内核中找到一些很好的例子。 看看下一个文件:

  • 司机/炭/ rtc.c
  • FS / PROC / kmsg.c
  • 司机/炭/ random.c

要将poll()函数添加到代码中,请执行下一步。

  1. 包括需要的标题:

     #include <linux/wait.h> #include <linux/poll.h> 
  2. 声明waitqueue变量:

     static DECLARE_WAIT_QUEUE_HEAD(fortune_wait); 
  3. 添加fortune_poll()函数并将其作为.poll回调添加到文件操作结构中:

     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 POLLIN | POLLRDNORM如果你有一些新的数据要读取, 0 ,如果没有新的数据要读取( poll()调用超时)。 详情请参阅man 2民意调查 。

  4. 一旦你有新的数据通知你的等待:

     wake_up_interruptible(&fortune_wait); 

这是实现poll()操作的基本内容。 根据你的任务,你可能需要在你的.read函数中使用一些waitqueue API (比如wait_event_interruptible() )。


另请参阅相关问题: 在Linux内核模块中实现轮询 。

最小的可运行示例

用法:

 insmod /poll.ko mount -t debugfs none /sys/kernel/debug /poll.out /sys/kernel/debug/lkmc_poll/f 

结果:每一秒钟,屏幕上都会显示以下内容:

 loop POLLIN n=10 buf=<jiffies> 

GitHub上游与QEMU + Buildroot样板: poll.ko , poll.out

在这个简化的例子中,我们从一个单独的线程生成轮询事件。 在现实生活中,轮询事件很可能会由于中断,硬件完成一些工作,新数据可供用户读取而触发。

poll.ko:

 #include <asm/uaccess.h> /* copy_from_user, copy_to_user */ #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/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible */ #include <uapi/linux/stat.h> /* S_IRUSR */ MODULE_LICENSE("GPL"); static char readbuf[1024]; static size_t readbuflen; static struct dentry *dir; 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. This gets called again every time an event happens in the wait queue. */ unsigned int poll(struct file *filp, struct poll_table_struct *wait) { poll_wait(filp, &waitqueue, wait); if (readbuflen) return POLLIN; else 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); wake_up(&waitqueue); } return 0; } static const struct file_operations fops = { .owner = THIS_MODULE, .read = read, .poll = poll }; static int myinit(void) { dir = debugfs_create_dir("lkmc_poll", 0); debugfs_create_file("f", S_IRUSR | S_IWUSR, dir, 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_recursive(dir); } module_init(myinit) module_exit(myexit) 

poll.out userland:

 #define _XOPEN_SOURCE 700 #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], path[1024]; int fd, i, n; short revents; struct pollfd pfd; 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("loop"); i = poll(&pfd, 1, -1); if (i == -1) { perror("poll"); exit(EXIT_FAILURE); } revents = pfd.revents; if (revents & POLLIN) { n = read(pfd.fd, buf, sizeof(buf)); printf("POLLIN n=%d buf=%.*s\n", n, n, buf); } } }