从内核模块写入eventfd

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

下面是我想要做的:我正在开发一个用户空间程序,它需要用我写的内核空间模块交换数据和信号。 为了传输数据,我已经使用ioctl。 但是,我希望内核模块能够在新数据准备好消耗ioctl时发送信号给用户空间程序。

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

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

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


我终于想出了如何做到这一点。 我意识到系统上的每个打开的文件都可以通过其中一个打开它的进程的pid和与该文件相对应的fd(在该进程的上下文中)来标识。 因此,如果我的内核模块知道pid和fd,它可以查找进程的struct * task_struct ,并从该结构*文件中最后使用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=%dn",pid,efd);

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

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

    eventfd_signal(efd_ctx, plus_one);

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

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

要执行此操作,请执行以下步骤:

  • 编译用户空间程序(efd_us.out)和内核模块(efd_lkm.ko)
  • 运行用户空间程序(./efd_us.out)并记下它打印的pid和efd值。 (例如,“pid = 2803 efd = 3”。用户空间程序将在select()
  • 打开一个新的终端窗口并插入传递pid和efd作为参数的内核模块: sudo insmod efd_lkm.ko pid = 2803 efd = 3
  • 切换回用户空间程序窗口,您将看到用户空间程序已经脱离了select和exited。

  • 在这里查看内核源代码:

    http://lxr.free-electrons.com/source/fs/eventfd.c

    基本上,通过ioctl()或其他路径将您的用户空间文件描述符eventfd()eventfd()生成eventfd()发送到您的模块。 在内核中,调用eventfd_ctx_fdget()以获取eventfd上下文,然后eventfd_signal()生成上下文。 当你完成上下文时,不要忘记eventfd_ctx_put()


    我如何解决来自内核空间的这些eventfds的“struct file *”指针

    您必须将这些指针解析为您创建的此接口已发布的数据结构(创建新类型并从struct file读取想要的字段)。

    有没有更好的方式从内核空间向用户空间发送事件信号?

    Netlink套接字是内核与用户空间进行通信的另一种便捷方式。 “更好”是旁观者的眼睛。

    链接地址: http://www.djcxy.com/p/87883.html

    上一篇: Writing to eventfd from kernel module

    下一篇: hrtimer repeating task in the Linux kernel