一起来感受下eventfd的魅力(二kernel通过eventfd向user空间发送消息事件通知)

Posted 高桐@BILL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起来感受下eventfd的魅力(二kernel通过eventfd向user空间发送消息事件通知)相关的知识,希望对你有一定的参考价值。

写在前面

接下来我们来尝试写一个测试程序,该测试程序首先在用户空间创建eventfd对象,然后再通过kernel模块程序更新eventfd的计数器以实现向用户控件程序发送事件消息通知。

一、用户空间程序代码

1.1 源码

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

1.2 编译方法

gcc efd_us.c -o efd_us

二、内核模块代码

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


三、 编译对应Ubuntu版本的内核源码

3.1 更新sources.list

deb-src http://archive.ubuntu.com/ubuntu bionic main
deb-src http://archive.ubuntu.com/ubuntu bionic-updates main

3.2 安装必须的包

sudo apt-get build-dep linux linux-image-$(uname -r)

3.3 安装必须工具链

sudo apt-get install libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf git

编译内核模块

make modules

如:make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules

直接make,不加任何参数,就是make all,包含make modules。

make modules是单独编译模块,驱动被配置成M的都是modules,modules不会被编译进内核image,需要单独安装到rootfs。

3.4 下载当前内核对应源码

apt-get source linux-image-unsigned-$(uname -r)

或 git clone git://kernel.ubuntu.com/ubuntu/ubuntu-<release codename>.git

3.5 修改配置

chmod a+x debian/rules
chmod a+x debian/scripts/*
chmod a+x debian/scripts/misc/*
LANG=C fakeroot debian/rules clean
LANG=C fakeroot debian/rules editconfigs # you need to go through each (Y, Exit, Y, Exit..) or get a complaint about config later

3.6 编译内核·

LANG=C fakeroot debian/rules clean
# quicker build:
LANG=C fakeroot debian/rules binary-headers binary-generic binary-perarch
# if you need linux-tools or lowlatency kernel, run instead:
LANG=C fakeroot debian/rules binary

注意!可复制系统当前配置文件(没有试过)

bill@bill-VirtualBox:/boot$ sudo cp config-5.4.0-84-generic ../usr/src/linux-headers-5.4.0-84-generic/.config

四、FAQ

4.1 执行“apt-get source linux-image-$(uname -r)”,提示"E:您必须在sources.list中指定源代码(deb-src)URI"

解决方案:在软件和更新(ubuntu)勾选“源代码”

以上是关于一起来感受下eventfd的魅力(二kernel通过eventfd向user空间发送消息事件通知)的主要内容,如果未能解决你的问题,请参考以下文章

云栖时间!一起来感受数字技术创造的魅力

Intel 计划在Linux kernel中引入 User Interrupts,效率是eventfd的10倍

Intel 计划在Linux kernel中引入 User Interrupts,效率是eventfd的10倍

手写自定义springboot-starter,感受框架的魅力和原理

自定义springboot-starter,感受框架的魅力和原理

走进线性表,初步感受数据结构的魅力