从内核模块写入eventfd

我已经使用eventfd()在用户空间程序中创build了一个eventfd实例。 有没有办法,我可以通过一些引用(一个指向它的struct或pid + fd对的指针)这个创build的eventfd实例到一个内核模块,以便它可以更新计数器值?

这是我想要做的:我正在开发一个用户空间程序,需要与我写的内核空间模块交换数据和信号。 为了传输数据,我已经使用ioctl。 但是我希望内核模块能够在新数据准备好用于通过ioctl消费时发信号通知用户空间程序。

为此,我的用户空间程序将在各个线程中创build一些eventfds。 这些线程将使用select()等待这些eventfds,并且每当内核模块更新这些eventfds上的计数时,它们将继续通过请求ioctl来使用这些数据。

问题是,如何解决从kernelspace到这些eventfds的“struct file *”指针? 我可以发送什么样的信息给事件发送到内核模块,以便它可以获取指向eventfds的指针? 我将在内核模块中使用哪些函数来获取这些指针?

有没有更好的方式从内核空间向用户空间发送事件信号? 我不能放弃使用select()。

我终于想出了如何做到这一点。 我意识到,系统上的每个打开的文件都可以通过打开它的某个进程的pid和该文件(在该进程的上下文中)对应的fd来标识。 所以如果我的内核模块知道了pid和fd,它可以查找进程的struct * task_struct ,并从那个struct *文件中最后使用fd,它可以获取指向eventfd的struct *文件的指针。 然后,使用这个最后的指针,它可以写入eventfd的计数器。

下面是用户空间程序和内核模块的代码,我写了这些代码来演示这个概念(现在可以工作):

用户空间C代码(efd_us.c):

#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> //Definition of uint64_t #include <sys/eventfd.h> int efd; //Eventfd file descriptor uint64_t eftd_ctr; int retval; //for select() fd_set rfds; //for select() int s; int main() { //Create eventfd efd = eventfd(0,0); if (efd == -1){ printf("\nUnable to create eventfd! Exiting...\n"); exit(EXIT_FAILURE); } printf("\nefd=%d pid=%d",efd,getpid()); //Watch efd FD_ZERO(&rfds); FD_SET(efd, &rfds); printf("\nNow waiting on select()..."); fflush(stdout); retval = select(efd+1, &rfds, NULL, NULL, NULL); if (retval == -1){ printf("\nselect() error. Exiting..."); exit(EXIT_FAILURE); } else if (retval > 0) { printf("\nselect() says data is available now. Exiting..."); printf("\nreturned from select(), now executing read()..."); s = read(efd, &eftd_ctr, sizeof(uint64_t)); if (s != sizeof(uint64_t)){ printf("\neventfd read error. Exiting..."); } else { printf("\nReturned from read(), value read = %lld",eftd_ctr); } } else if (retval == 0) { printf("\nselect() says that no data was available"); } printf("\nClosing eventfd. Exiting..."); close(efd); printf("\n"); exit(EXIT_SUCCESS); } 

内核模块C代码(efd_lkm.c):

 #include <linux/module.h> #include <linux/kernel.h> #include <linux/pid.h> #include <linux/sched.h> #include <linux/fdtable.h> #include <linux/rcupdate.h> #include <linux/eventfd.h> //Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object. int pid; int efd; //Resolved references... struct task_struct * userspace_task = NULL; //...to userspace program's task struct struct file * efd_file = NULL; //...to eventfd's file struct struct eventfd_ctx * efd_ctx = NULL; //...and finally to eventfd context //Increment Counter by 1 static uint64_t plus_one = 1; int init_module(void) { printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd); userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID); printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task); printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files); rcu_read_lock(); efd_file = fcheck_files(userspace_task->files, efd); rcu_read_unlock(); printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file); efd_ctx = eventfd_ctx_fileget(efd_file); if (!efd_ctx) { printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n"); return -1; } printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx); eventfd_signal(efd_ctx, plus_one); printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n"); eventfd_ctx_put(efd_ctx); return 0; } void cleanup_module(void) { printk(KERN_ALERT "~~~modulee Exiting...\n"); } MODULE_LICENSE("GPL"); module_param(pid, int, 0); module_param(efd, int, 0); 

要执行此操作,请执行以下步骤:

  1. 编译用户空间程序(efd_us.out)和内核模块(efd_lkm.ko)
  2. 运行用户空间程序(./efd_us.out)并记下它打印的pid和efd值。 (例如“pid = 2803 efd = 3”,用户空间程序将在select()
  3. 打开一个新的终端窗口,并插入传递pid和efd作为参数的内核模块: sudo insmod efd_lkm.ko pid = 2803 efd = 3
  4. 切换回用户空间程序窗口,你会看到用户空间程序已经打破了选择和退出。

在这里查看内核源码:

http://lxr.free-electrons.com/source/fs/eventfd.c

基本上,通过ioctl()或其他路径将您的用户空间文件描述符eventfd()eventfd()生成eventfd()发送到您的模块。 从内核中调用eventfd_ctx_fdget()来获得一个eventfd上下文,然后调用eventfd_ctx_fdget()来获得上下文。 当你完成上下文时,不要忘记eventfd_ctx_put()

我如何解决kernelspace中这些eventfds的“struct file *”指针

您必须将这些指针解析为您创建的这个接口已经发布的数据结构(创建新的类型并从struct file读取所需的字段)。

有没有更好的方式从内核空间向用户空间发送事件信号?

Netlink套接字是内核与用户空间进行通信的另一种便捷方式。 在旁观者眼中,“更好”。