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

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

问题描述

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

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的结构文件*"指针?我可以向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 *文件中查找并最终使用fd,可以获取指向eventfd的 struct *文件的指针.然后,使用最后一个指针,它可以写入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("\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);

要运行此程序,请执行以下步骤:

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. 切换回用户空间程序窗口,您将看到用户空间程序已脱离选择范围并退出.
  1. Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
  2. 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()
  3. 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
  4. Switch back to the userspace program window and you will see that the userspace program has broken out of select and exited.

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

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