内核态与用户态通信之eventfd使用

首先需要确定eventfd已经被编译进内核,其次还要确定所使用的交叉编译器支持eventfd。

函数原型:

#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);

说明:initval的范围是0~0xfffffffffffffffe;flags的值可以是如下枚举值:

enum
{
    EFD_SEMAPHORE = 00000001,//since Linux 2.6.30
#define EFD_SEMAPHORE EFD_SEMAPHORE
    EFD_CLOEXEC = 02000000,//since Linux 2.6.27
#define EFD_CLOEXEC EFD_CLOEXEC
    EFD_NONBLOCK = 00004000//since Linux 2.6.27
#define EFD_NONBLOCK EFD_NONBLOCK
};

如果设置了EFD_SEMAPHORE,则不会出现粘包的情况,即write多少次,就需要read多少次;

如果设置了0(内核2.6.27版本之前必须设置为0),则会出现粘包的可能,write多次,可能read到的是若干次的值的和;

另外两种一般不需要,所以并未进行测试。

  1. 用户态

    #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);
    }
    
  2. 内核态

    #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);
    
  3. 如何使用以上示例

Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
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()
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
Switch back to the userspace program window and you will see that the userspace program hasbroken out of select and exited.

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Linux 系统中,eventfd 可以通过事件循环机制实现。事件循环机制是一个通用的机制,可以用于管理多种类型的事件和文件描述符,包括 socket、pipe、timer 等。它的核心是一个等待事件发生的循环,当有事件发生时,循环将调用相应的处理函数进行处理。 eventfd 本质上是一个文件描述符,它可以被添加到事件循环中进行监控。在 Linux 系统中,事件循环机制通过 epoll 系统调用来实现。 下面是一个使用事件循环机制实现的 eventfd 示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #include <sys/eventfd.h> #define MAX_EVENTS 10 int main() { int efd, nfds, i; struct epoll_event ev, events[MAX_EVENTS]; uint64_t count = 0; // 创建 eventfd 文件描述符 efd = eventfd(0, EFD_NONBLOCK); if (efd == -1) { perror("eventfd"); exit(EXIT_FAILURE); } // 创建 epoll 实例 int epfd = epoll_create1(0); if (epfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } // 将 eventfd 添加到 epoll 监控列表中 ev.events = EPOLLIN | EPOLLET; ev.data.fd = efd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &ev) == -1) { perror("epoll_ctl: efd"); exit(EXIT_FAILURE); } // 等待事件发生 while (1) { nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } // 处理所有事件 for (i = 0; i < nfds; i++) { if (events[i].data.fd == efd) { // 读取 eventfd 中的计数器值 if (read(efd, &count, sizeof(uint64_t)) == -1) { perror("read"); exit(EXIT_FAILURE); } printf("eventfd count: %llu\n", (unsigned long long) count); } } } close(efd); return 0; } ``` 在上面的代码中,首先创建了一个 eventfd 文件描述符,并将其添加到 epoll 实例中进行监控。然后,进入一个无限循环,等待事件的发生。当有事件发生时,通过 epoll_wait 函数获取事件,然后处理所有事件。如果是 eventfd 文件描述符的事件,则读取其中的计数器值,并进行处理。 需要注意的是,在使用 eventfd 时,必须将其设置为非阻塞模式(EFD_NONBLOCK),否则可能会导致阻塞进程。此外,eventfd 的计数器值必须是一个 64 位的无符号整数,因此在读取时需要使用 uint64_t 类型进行存储。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值