从内核模块写入eventfd

我已经使用eventfd()在用户空间程序中创建了一个eventfd实例。有没有一种方法可以将某些引用(指向其struct或pid +

fd对的指针)传递给已创建的eventfd实例到内核模块,以便它可以更新计数器值?

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

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

问题是,如何从内核空间解析指向这些eventfds的“结构文件*”指针?我可以向eventfds发送什么样的信息,以便它可以获取指向eventfds的指针?我将在内核模块中使用哪些函数来获取这些指针?

有没有更好的方法可以将事件从内核空间发送到用户空间?我不能放手使用select()。

回答:

我终于想出了怎么做。我意识到,系统上的每个打开文件都可以由打开该文件的进程之一的pid以及与该文件相对应的fd标识(在该进程的上下文中)。因此,如果我的内核模块知道pid和fd,则可以查找进程的

,并从中查找 ,最后使用fd,它可以获取指向eventfd的

的指针。然后,使用最后一个指针,它可以写入eventfd的计数器。

这是我为演示该概念而编写的用户空间程序和内核模块的代码(现已可用):

#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);

}

#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. 编译用户空间程序(efd_us.out)和内核模块(efd_lkm.ko)
  2. 运行用户空间程序(./efd_us.out),并注意其打印的pid和efd值。(例如,“ pid = 2803 efd = 3”。用户空间程序将在select()上无限等待
  3. 打开一个新的终端窗口,并插入将pid和efd作为参数传递的内核模块:
  4. 切换回用户空间程序窗口,您将看到用户空间程序已脱离选择并退出。

以上是 从内核模块写入eventfd 的全部内容, 来源链接: utcz.com/qa/419706.html

回到顶部