我使用eventfd()在用户空间程序中创建了一个eventfd实例。有没有一种方法可以将一些引用(指向其struct或pid + fd对的指针)传递给这个创建的eventfd实例到内核模块,以便它可以更新计数器值?
这是我想要做的: 我正在开发一个用户空间程序,它需要与我编写的内核空间模块交换数据和信号。 为了传输数据,我已经在使用ioctl了。但是我希望内核模块能够在新数据准备就绪时通过ioctl消耗用户空间程序。
为此,我的用户空间程序将在各种线程中创建一些eventfds。这些线程将使用select()等待这些eventfds,并且每当内核模块更新这些eventfds上的计数时,它们将通过ioctl请求它来继续使用数据。
问题是,如何解决来自kernelspace的这些eventfds的“struct file *”指针?我可以将什么样的信息发送到内核模块,以便它可以获取指向eventfds的指针?我将在内核模块中使用哪些函数来获取这些指针?
有没有更好的方法从内核空间向用户空间发送信号? 我不能放弃使用select()。
答案 0 :(得分:18)
我终于想出了如何做到这一点。我意识到系统上的每个打开文件都可以通过打开它的一个进程的pid和对应于该文件的fd(在该进程的上下文中)来识别。因此,如果我的内核模块知道pid和fd,它可以查找进程的 struct * task_struct ,然后查找 struct * files ,最后使用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 "~~~Module Exiting...\n");
}
MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
要运行此功能,请执行以下步骤:
答案 1 :(得分:8)
请在此处查阅内核源代码:
http://lxr.free-electrons.com/source/fs/eventfd.c
基本上,通过eventfd()
或其他路径将您的用户空间文件描述符(由ioctl()
生成)发送到您的模块。从内核中,调用eventfd_ctx_fdget()
以获取eventfd上下文,然后在结果上下文中调用eventfd_signal()
。完成上下文后,请不要忘记eventfd_ctx_put()
。
答案 2 :(得分:1)
如何从kernelspace
解析指向这些eventfds的“struct file *”指针
您必须将这些指针解析为您创建的此接口已发布的数据结构(创建新类型并从struct file
中读取所需的字段)。
是否有更好的方法从内核空间向用户空间发送信号?
Netlink sockets是内核与用户空间通信的另一种便捷方式。 “更好”是旁观者的眼睛。