【eventfd使用案例】

文章介绍了eventfd作为轻量级的进程间通信机制,提供了两个案例,一个是用户态进程间的通信,另一个是用户态与内核态的通信。示例代码展示了如何创建、读写eventfd以及如何与select或epoll配合使用。
摘要由CSDN通过智能技术生成

Eventfd使用

众所周知进程间通信有很多种方式,eventfd 应该是开销小,效率高的一种方式

案例一用户态进程间通信

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>     //Definition of uint64_t
#include <sys/eventfd.h>

#include <sys/epoll.h>
#include <errno.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <string.h>

struct pid_info
{
    int pid;
    int ppid;
};

int main(int argc, char *argv[])
{
    int efd;
    struct pid_info pinfo;
    struct pid_info tmppinfo;
    ssize_t s;

    //创建一个eventfd对象,返回一个文件描述符
    efd = eventfd(0, 0);
    memset(&pinfo, 0x0, sizeof(struct pid_info));
    memset(&tmppinfo, 0x0, sizeof(struct pid_info));

    switch (fork())
    {
        case 0: //子进程
            //sleep(2); //先休眠2秒,等待子进程写完数据
            pinfo.pid = getpid();
            pinfo.ppid = 1;
            printf("Child writing to efd pid:%d\n",pinfo.pid);
                //向eventfd内部写一个8字节大小的数据
            s = write(efd, &pinfo, sizeof(struct pid_info));
            printf("Child completed write begin read\n");
            s = read(efd, &pinfo, sizeof(struct pid_info));
            printf("Child reading to efd pid:%d\n",pinfo.pid);
            exit(EXIT_SUCCESS);

       default: //父进程
           printf("Parent about to read\n");
           //从eventfd中读取数据
           s = read(efd, &tmppinfo, sizeof(struct pid_info));
           printf("Parent reading to efd pid:%d\n",tmppinfo.pid);
           printf("Parent about to write\n");
           tmppinfo.pid = getpid();
           s = write(efd, &tmppinfo, sizeof(struct pid_info));
           exit(EXIT_SUCCESS);
           break;

       case -1:
           printf("error\n");
           break;
           //handle_error("fork");
    }
}

案例二,用户态和内核态通信

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

#include <sys/eventfd.h>
int eventfd(unsigned int initval, int 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到的是若干次的值的和;

另外两种一般不需要,所以并未进行测试。
用户态代码:

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

上述select可以用epoll来替代,实现效率更高。
内核态代码

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值