从内核模块写入 eventfd [英] Writing to eventfd from kernel module

查看:18
本文介绍了从内核模块写入 eventfd的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 eventfd() 在用户空间程序中创建了一个 eventfd 实例.有没有办法可以将一些引用(指向其结构或 pid+fd 对的指针)传递给这个创建的 eventfd 实例到内核模块,以便它可以更新计数器值?

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?

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

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.

为此,我的用户空间程序将在各个线程中创建一些 eventfds.这些线程将使用 select() 等待这些 eventfds,每当内核模块更新这些 eventfds 上的计数时,它们将继续通过 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.

问题是,如何从内核空间解析指向这些 eventfds 的struct file *"指针?我可以将哪些关于 eventfds 的信息发送到内核模块,以便它可以获取指向 eventfds 的指针?我将在内核模块中使用哪些函数来获取这些指针?

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?

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

Is there better way to signal events to userspace from kernelspace? I cannot let go of using select().

推荐答案

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

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.

以下是我为演示这个概念而编写的用户空间程序和内核模块的代码(现在可以使用了):

Here are the codes for the userspace program and the kernel module that I wrote up to demonstrate the concept (which now work):

用户空间 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("
Unable to create eventfd! Exiting...
");
        exit(EXIT_FAILURE);
    }

    printf("
efd=%d pid=%d",efd,getpid());

    //Watch efd
    FD_ZERO(&rfds);
    FD_SET(efd, &rfds);

    printf("
Now waiting on select()...");
    fflush(stdout);

    retval = select(efd+1, &rfds, NULL, NULL, NULL);

    if (retval == -1){
        printf("
select() error. Exiting...");
        exit(EXIT_FAILURE);
    } else if (retval > 0) {
        printf("
select() says data is available now. Exiting...");
        printf("
returned from select(), now executing read()...");
        s = read(efd, &eftd_ctr, sizeof(uint64_t));
        if (s != sizeof(uint64_t)){
            printf("
eventfd read error. Exiting...");
        } else {
            printf("
Returned from read(), value read = %lld",eftd_ctr);
        }
    } else if (retval == 0) {
        printf("
select() says that no data was available");
    }

    printf("
Closing eventfd. Exiting...");
    close(efd);
    printf("
");
    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
",pid,efd);

    userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p
",userspace_task);

    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p
",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
",efd_file);


    efd_ctx = eventfd_ctx_fileget(efd_file);
    if (!efd_ctx) {
        printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.
");
        return -1;
    }
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p
",efd_ctx);

    eventfd_signal(efd_ctx, plus_one);

    printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1
");

    eventfd_ctx_put(efd_ctx);

    return 0;
}


void cleanup_module(void) {
    printk(KERN_ALERT "~~~Module Exiting...
");
}  

MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);

要运行它,请执行以下步骤:

To run this, carry out the following steps:

  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. 切换回用户空间程序窗口,您将看到用户空间程序已脱离选择并退出.

这篇关于从内核模块写入 eventfd的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆