I have created an eventfd instance in a userspace program using eventfd(). Is there a way in which I can pass some reference (a pointer to its struct or pid+fd pair) to this created instance of eventfd to a kernel module so that it can update the counter value?
我使用eventfd()在用户空间程序中创建了一个eventfd实例。是否有一种方法可以将某个引用(指向它的struct或pid+fd对的指针)传递到这个创建的eventfd实例到内核模块,以便它能够更新计数器值?
Here is what I want to do: I am developing a userspace program which needs to exchange data and signals with a kernel space module which I have written. For transferring data, I am already using ioctl. But I want the kernel module to be able to signal the userspace program whenever new data is ready for it to consume over ioctl.
下面是我想做的:我正在开发一个用户空间程序,它需要用我编写的内核空间模块来交换数据和信号。对于传输数据,我已经在使用ioctl。但是,我希望内核模块能够在新数据准备使用ioctl时向用户空间程序发出信号。
To do this, my userspace program will create a few eventfds in various threads. These threads will wait on these eventfds using select() and whenever the kernel module updates the counts on these eventfds, they will go on to consume the data by requesting for it over ioctl.
为此,我的userspace程序将在不同的线程中创建几个eventfd。这些线程将使用select()对这些eventfds进行等待,当内核模块更新这些eventfds上的计数时,它们将继续通过ioctl请求数据来使用这些数据。
The problem is, how do I resolve the "struct file *" pointers to these eventfds from kernelspace? What kind of information bout the eventfds can I sent to kernel modules so that it can get the pointers to the eventfds? what functions would I use in the kernel module to get those pointers?
问题是,如何从内核空间解析指向这些eventfds的“struct file *”指针?我可以向内核模块发送关于eventfds的哪些信息,以便它能够获得指向eventfds的指针?我将在内核模块中使用哪些函数来获得这些指针?
Is there better way to signal events to userspace from kernelspace? I cannot let go of using select().
有更好的方法将事件从内核空间发送到用户空间吗?我不能放弃使用select()。
3 个解决方案
#1
17
I finally figured out how to do this. I realized that each open file on a system could be identified by the pid of one of the processes which opened it and the fd corresponding to that file (within that process's context). So if my kernel module knows the pid and fd, it can look up the struct * task_struct of the process and from that the struct * files and finally using the fd, it can acquire the pointer to the eventfd's struct * file. Then, using this last pointer, it can write to the eventfd's counter.
我终于知道怎么做了。我意识到,系统中的每个打开文件都可以通过打开它的进程的pid和对应于该文件的fd(在该进程的上下文中)来识别。因此,如果我的内核模块知道pid和fd,它可以查找进程的struct * task_struct,并从中查找struct *文件,最后使用fd,它可以获取指向eventfd的struct *文件的指针。然后,使用最后的指针,它可以写入eventfd的计数器。
Here are the codes for the userspace program and the kernel module that I wrote up to demonstrate the concept (which now work):
下面是我为演示这个概念而编写的userspace程序和内核模块的代码(现在可以工作了):
Userspace C code (efd_us.c):
用户空间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);
}
Kernel Module C code (efd_lkm.c):
内核模块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 "~~~Module Exiting...\n");
}
MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
To run this, carry out the following steps:
为此,执行以下步骤:
- Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
- 编译userspace程序(efd_us.out)和内核模块(efd_lkm.ko)
- Run the userspace program (./efd_us.out) and note the pid and efd values that it print. (for eg. "pid=2803 efd=3". The userspace program will wait endlessly on select()
- 运行userspace程序(./efd_us.out)并注意它打印的pid和efd值。(如。“pid = 2803使用efd = 3”。userspace程序将在select()上无止境地等待
- Open a new terminal window and insert the kernel module passing the pid and efd as params: sudo insmod efd_lkm.ko pid=2803 efd=3
- 打开一个新的终端窗口并插入内核模块,通过pid和efd作为params: sudo insmod efd_lkm。ko pid = 2803使用efd = 3
- Switch back to the userspace program window and you will see that the userspace program has broken out of select and exited.
- 切换回userspace程序窗口,您将看到userspace程序已经跳出select并退出。
#2
5
Consult the kernel source here:
请参阅这里的内核源代码:
http://lxr.free-electrons.com/source/fs/eventfd.c
http://lxr.free-electrons.com/source/fs/eventfd.c
Basically, send your userspace file descriptor, as produced by eventfd()
, to your module via ioctl()
or some other path. From the kernel, call eventfd_ctx_fdget()
to get an eventfd context, then eventfd_signal()
on the resulting context. Don't forget eventfd_ctx_put()
when you're done with the context.
基本上,通过ioctl()或其他路径将userspace文件描述符(由eventfd()生成)发送到模块。从内核调用eventfd_ctx_fdget()获取eventfd上下文,然后在结果上下文中调用eventfd_signal()。在完成上下文之后,不要忘记eventfd_ctx_put()。
#3
1
how do I resolve the "struct file *" pointers to these eventfds from kernelspace
如何解析从内核空间指向这些eventfds的“struct file *”指针
You must resolve those pointers into data structures that this interface you've created has published (create new types and read the fields you want from struct file
into it).
您必须将这些指针解析为您创建的接口已发布的数据结构(创建新类型并从struct文件中读取您想要的字段)。
Is there better way to signal events to userspace from kernelspace?
有更好的方法将事件从内核空间发送到用户空间吗?
Netlink sockets are another convenient way for the kernel to communicate with userspace. "Better" is in the eye of the beholder.
Netlink套接字是内核与用户空间通信的另一种方便方式。情人眼里出西施。
#1
17
I finally figured out how to do this. I realized that each open file on a system could be identified by the pid of one of the processes which opened it and the fd corresponding to that file (within that process's context). So if my kernel module knows the pid and fd, it can look up the struct * task_struct of the process and from that the struct * files and finally using the fd, it can acquire the pointer to the eventfd's struct * file. Then, using this last pointer, it can write to the eventfd's counter.
我终于知道怎么做了。我意识到,系统中的每个打开文件都可以通过打开它的进程的pid和对应于该文件的fd(在该进程的上下文中)来识别。因此,如果我的内核模块知道pid和fd,它可以查找进程的struct * task_struct,并从中查找struct *文件,最后使用fd,它可以获取指向eventfd的struct *文件的指针。然后,使用最后的指针,它可以写入eventfd的计数器。
Here are the codes for the userspace program and the kernel module that I wrote up to demonstrate the concept (which now work):
下面是我为演示这个概念而编写的userspace程序和内核模块的代码(现在可以工作了):
Userspace C code (efd_us.c):
用户空间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);
}
Kernel Module C code (efd_lkm.c):
内核模块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 "~~~Module Exiting...\n");
}
MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
To run this, carry out the following steps:
为此,执行以下步骤:
- Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
- 编译userspace程序(efd_us.out)和内核模块(efd_lkm.ko)
- Run the userspace program (./efd_us.out) and note the pid and efd values that it print. (for eg. "pid=2803 efd=3". The userspace program will wait endlessly on select()
- 运行userspace程序(./efd_us.out)并注意它打印的pid和efd值。(如。“pid = 2803使用efd = 3”。userspace程序将在select()上无止境地等待
- Open a new terminal window and insert the kernel module passing the pid and efd as params: sudo insmod efd_lkm.ko pid=2803 efd=3
- 打开一个新的终端窗口并插入内核模块,通过pid和efd作为params: sudo insmod efd_lkm。ko pid = 2803使用efd = 3
- Switch back to the userspace program window and you will see that the userspace program has broken out of select and exited.
- 切换回userspace程序窗口,您将看到userspace程序已经跳出select并退出。
#2
5
Consult the kernel source here:
请参阅这里的内核源代码:
http://lxr.free-electrons.com/source/fs/eventfd.c
http://lxr.free-electrons.com/source/fs/eventfd.c
Basically, send your userspace file descriptor, as produced by eventfd()
, to your module via ioctl()
or some other path. From the kernel, call eventfd_ctx_fdget()
to get an eventfd context, then eventfd_signal()
on the resulting context. Don't forget eventfd_ctx_put()
when you're done with the context.
基本上,通过ioctl()或其他路径将userspace文件描述符(由eventfd()生成)发送到模块。从内核调用eventfd_ctx_fdget()获取eventfd上下文,然后在结果上下文中调用eventfd_signal()。在完成上下文之后,不要忘记eventfd_ctx_put()。
#3
1
how do I resolve the "struct file *" pointers to these eventfds from kernelspace
如何解析从内核空间指向这些eventfds的“struct file *”指针
You must resolve those pointers into data structures that this interface you've created has published (create new types and read the fields you want from struct file
into it).
您必须将这些指针解析为您创建的接口已发布的数据结构(创建新类型并从struct文件中读取您想要的字段)。
Is there better way to signal events to userspace from kernelspace?
有更好的方法将事件从内核空间发送到用户空间吗?
Netlink sockets are another convenient way for the kernel to communicate with userspace. "Better" is in the eye of the beholder.
Netlink套接字是内核与用户空间通信的另一种方便方式。情人眼里出西施。