linux eventfd事件通知 比信号量更好用

  


目录

前言

概述

原理简介

使用场景

接口说明

头文件

参数说明

代码演示

默认参数

信号量模式

结尾


前言

本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。


概述

eventfd 就是一个用于事件通知的fd。当然linux中,一切都可以做为文件来看待,所以就有fd。这样有一个好处管理统一,比如可以加入到epoll事件等待中。很多人可能没怎么用,但是用过的人都说:香 !

原理简介

eventfd 提供了一个进程/线程间通信的方式。

这个方式是载体是一个句柄,也就是fd,类型是eventfd,可以在/proc下查看fd时看到;

通过它可以传递事件信息,事件就是write次数累计,这个累计值用一个8字节整型值来记录,每次write时这个整型值自动会累计,它是由内核来维护,read时就会拿到累计值,并清零fd中的值。

当然这个eventfd,可以传入poll来监听,监听可读可写事件。

可写事件, eventfd的write是一直可以的,它可以不断累加,所以一直会是可写状态,所以可以不用理会可写事件。

可读事件,当eventfd的累计值为零时,为不可读状态,当大于零时,才可读。这样就提供了一种通知机制。

使用场景

适用于等待-通知的架构模式,类似于信号量的场景。

比如生产者准备好后,通知消费者;等待的消费者获取到通知后,进行消费;消费完后,消费者又开始等待。

那么,信号量也可以实现,与eventfd有什么区别呢?

两者都是内核变量,eventfd的优势在于,它可以作为文件fd来读写,同时还可以使用poll/select实现异步等待,也就是说eventfd在等待时,你还可以干别的事,如果有可读时,你再去读;而信号量则不行,只能是等待或者不等待。

接口说明

头文件

 #include <sys/eventfd.h>

创建eventfd类型的句柄

int eventfd(unsigned int initval, int flags);

参数说明

initval

计数的初值

flags

EFD_CLOEXEC:

在调用exec创建进程时会自动关闭fd。

EFD_NONBLOCK:

创建非阻塞模式的fd,也就是在计数为0时,不会等待,直接返回-1;不指定时,默认为阻塞模式。

EFD_SEMAPHORE:

创建类似信号量的模式,如果计数大于0时,每次读到的是1,同时计数自动减1,到0时阻塞。

不指定时,每次读全部累计值,计数被重置为0。

代码演示

默认参数

先看一个默认参数,默认是阻塞模式,每次都会取累计值,并重置eventfd中的累计值为零;如果累计值为0,则会阻塞。

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

int main(int argc, char *argv[])
{
        int efd;
        uint64_t u = 0;
        ssize_t s;

    efd = eventfd(0, 0);
    if (efd == -1)
                return -1;

        u = 0x01;
        printf("write eventfd %llu \n", u);
    s = write(efd, &u, sizeof(uint64_t));

        u = 0x02;
        printf("write eventfd %llu \n", u);
        s = write(efd, &u, sizeof(uint64_t));

        u = 0x03;
        printf("write eventfd %llu \n", u);
        s = write(efd, &u, sizeof(uint64_t));

        s = read(efd, &u, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
        {
                printf("read failure.\n");
        }

        printf("read %llu from efd\n", u);

        close(efd);

        return 0;
}

信号量模式

再来看一下使用信号量模式的效果,虽然写入了几次,但是每次read只读出1,累计值也是每次都会减1;

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

int main(int argc, char *argv[])
{
        int efd;
        uint64_t u = 0;
        ssize_t s;

    efd = eventfd(0, EFD_SEMAPHORE);
    if (efd == -1)
                return -1;

        u = 0x01;
        printf("write eventfd %llu \n", u);
    s = write(efd, &u, sizeof(uint64_t));

        u = 0x02;
        printf("write eventfd %llu \n", u);
        s = write(efd, &u, sizeof(uint64_t));


        s = read(efd, &u, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
        {
                printf("read failure.\n");
        }

        printf("read %llu from efd\n", u);

        s = read(efd, &u, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
        {
                printf("read failure.\n");
        }

        printf("read %llu from efd\n", u);

        s = read(efd, &u, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
        {
                printf("read failure.\n");
        }

        printf("read %llu from efd\n", u);

        close(efd);

        return 0;
}


结尾

作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。

注:未经同意,不得转载!

猜你喜欢

转载自blog.csdn.net/senllang/article/details/131180717