linux新增特性eventfd

1.新内核版本为什么要增加eventfd?

首先说明的一点是eventfd是用来实现多进程或多线程的之间的事件通知的,那么我们在没接触eventfd之前用到的事件通知机制都有那些?

1.条件变量
2.管道

我们来逐一比较此俩中机制与eventfd的效果方面的好坏,首先,条件变量必须和互斥锁结合使用,使用起来麻烦,而且性能未必比eventfd好,其次条件变量不能像eventfd一样为I/O事件驱动,因此不能和服务器的I/O模式很好的融合,所以在某些时候不如eventfd好用

接着是管道,虽然管道能与I/O复用很好的融合,但很明显管道相比eventfd多用了一个文件描述符,而且管道的话内核还得给其管理的缓冲区,eventfd则不需要,所以单纯作为事件通知的话还是管道好用

2.event的主要接口

eventfd只有一个接口,形式如下

int eventfd(unsigned int initval, int flags);
//成功返回事件驱动的文件描述符

eventfd()创建一个文件描述符,这个文件描述符用户可以通过等待其可读来实现事件通知,该通知靠内核来响应用户空间的应用事件。上述接口的第一个参数是一个由内核来保持的64位计数器,这个计数器有参数initval来初始化,关于此计数器的影响我在下文中的具体实例中给大家演示,一般我们可将其设为0

第二个参数flags可以为EFD_NONBLOCK或EFD_CLOEXEC,其含义分别为阻塞文件描述符,和与普通文件的CLOEXEC标志一样的功能(这里不想详细将CLOEXEC有兴趣的我之前写文件的基本操作的博文里有它的详细解释)

3.具体实例

#include 
#include 
#include 
#include 

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

上述代码中,我们创建一个线程,通过主线程往fd里写数据,来通知另一个线程。
代码执行结果如下
linux新增特性eventfd_第1张图片

关于eventfd中的initval参数的作用,我的测试结果如下,前提把上述代码eventfd函数中的0改为3

linux新增特性eventfd_第2张图片
可以看出这个由内核管理的计数器,我们的初始化值,只会影响程序第一次buffer的值,后续buffer中的值依然为1.

你可能感兴趣的:(linux)