Linux 2.6.27版本开始,新增了不少系统调用,其中包括eventfd,它的主要是用于进程或者线程间的通信(如通知/等待机制的实现)。


函数原型:      

#include <sys/eventfd.h>

/*
 * function: 创建eventfd
 * @initval: 信号量的初始值
 * @flags: (2.6.27以上内核有效)可以设置一下标志位如果是2.6.26或之前版本的内核,flags 必须设置为0。
 *     EFD_NONBLOCK 是否阻塞
 *     EFD_CLOEXEC close-on-exec标记,当exec时是否关闭该文件
 * return value:文件描述符fd
int eventfd(unsigned int initval, int flags);

eventfd在内核里的核心是一个计数器counter,它是一个uint64_t的整形变量counter,初始值为initval。

1.read(eventfd_read)

  消费者需要对信号量进行down操作时,调用read从eventfd读即可。read返回值:

  如果当前counter > 0,那么read返回counter值,并重置counter为0;

  •   如果当前counter等于0,那么read 1)阻塞直到counter大于0;2)如果设置了NONBLOCK,那么返回-1,并设置errno为EAGAIN。

  可以看到,eventfd实现的资源是一次性消耗品,只允许一次read。

2.write(eventfd_write)

  生产者需要执行up操作时,调用write写一个64bit的整数value到eventfd即可。write返回值:

  •   counter最大能存储的值是 0xffff ffff ffff fffe(以max表示此值),那么write尝试将value加到counter上,如果结果超过max,那么write一直阻塞直到有read操作发生,或者返回-1并设置errno为EAGAIN。

  所以write可以多次连续调用,但read读一次即可清零。实质上它应该是一个二元信号量,只有0和非0两种状态。但是应用程序也可以利用counter的值实现自己的逻辑,比如每次write都加1,那么read就能够知道在两次调用之间有多少次write操作发生,也就表示对应的事件发生了多少次。


例子:

#include <sys/eventfd.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int fd;
uint64_t buffer;

void threadFunc(void)   //线程函数
{
int t;
while(1)
{
     t = read(fd,&buffer,sizeof(buffer));       //阻塞等待fd可读,及通知事件发生

     if(sizeof(buffer) < 8)
     {
        printf("buffer错误\n");
     }
     printf("t = %llu   buffer = %llu\n",t,buffer);
     if(t == 8)
     {
        printf("唤醒成功\n");
     }

}    
}

int main(void)
{
    uint64_t buf = 1;
    int ret;
    pthread_t tid;

    if((fd = eventfd(0,0)) == -1)   //创建事件驱动的文件描述符
    {
        printf("创建失败\n");
    }

    //创建线程
    if(pthread_create(&tid,NULL,threadFunc,NULL) < 0)
    {
        printf("线程创建失败\n");
    }

    while(1)
    {
        ret = write(fd,&buf,sizeof(buf));  //通过往fd里写东西来进行事件通知
        if(ret != 8)
        {
            printf("写错误\n");
        }
        sleep(2);                           //没2s通知一次
    }

    return 0;
}



Logo

更多推荐