eventfd

1. 

#include 
int eventfd(unsigned int initval, int flags); //创建eventfd

参数含义:
initval:创建eventfd时它所对应的64位计数器的初始值;
flags:eventfd文件描述符的标志,可由三种选项组成:EFD_CLOEXEC、EFD_NONBLOCK和EFD_SEMAPHORE。
EFD_CLOEXEC:表示返回的eventfd文件描述符在fork后exec其他程序时会自动关闭这个文件描述符;
EFD_NONBLOCK:设置返回的eventfd非阻塞;
EFD_SEMAPHORE表:表示将eventfd作为一个信号量来使用。
 

2.

例子

#include 
#include 
#include 
#include 
#include 
/* Definition of uint64_t */

#define handle_error(msg)   \
    do                      \
    {                       \
        perror(msg);        \
        exit(EXIT_FAILURE); \
    } while (0)

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

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s ...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    efd = eventfd(0, 0);
    if (efd == -1)
        handle_error("eventfd");

    switch (fork())
    {
    case 0:
        for (j = 1; j < argc; j++)
        {
            printf("Child writing %s to efd\n", argv[j]);
            u = strtoull(argv[j], NULL, 0);
            /* strtoull() allows various bases */
            s = write(efd, &u, sizeof(uint64_t)); // 每次写入会累加
            if (s != sizeof(uint64_t))
                handle_error("write");
        }
        printf("Child completed write loop\n");

        exit(EXIT_SUCCESS);

    default:
        sleep(2);

        printf("Parent about to read\n");
        s = read(efd, &u, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
            handle_error("read");
        printf("Parent read %llu (0x%llx) from efd\n",
               (unsigned long long)u, (unsigned long long)u);
        exit(EXIT_SUCCESS);

    case -1:
        handle_error("fork");
    }
}

其他用法,

一个消费者,多个生产的时候,

producer:
    // 投递请求到链表
    list_add( global_list, request )
    // 唤醒消费者处理
    write(eventfd, &cnt /* 1 */ , 8)

consumer 
    // 添加 eventfd 到监听池
    epoll_ctl(ep, EPOLL_CTL_ADD, eventfd, &ee);
 
loop:
    // 等待唤醒
    epoll_wait(ep, ... );
    
    // 读取新添加到列表里的元素个数,并且进行处理;
    n = read(eventfd, ... )
    // 遍历链表处理
    for each global_list:

一个消费者,一个生产者

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

用于在进程间传递事件信号,与 pipe 相比,eventfd 具有更高的性能和更好的可扩展性,可以支持更多的并发连接。

在 Nginx 的事件模块中,当系统支持 eventfd 时,会启用 eventfd 作为唤醒机制,来代替之前使用 pipe 和 signal 的方式。使用 eventfd 可以避免 signal 的问题(如慢 syscalls 可能被中断、signal 处理可能会导致竞争和死锁等),且相比于使用 pipe 的方式,可以减少内存消耗和增加性能。

eventfd不支持非亲缘关系进程之间的事件通知,因为 eventfd 使用的内核数据结构属于文件描述符表,而文件描述符表只能在同一进程内共享。

无名信号量也不支持非亲缘关系的进程

eventfd 和 条件变量.和信号量都差不多, 都是可以通知唤醒, 

.如果只是简单的事件通知或计数,且不需要对共享资源进行保护,那么 eventfd 可能更为高效

如果需要等待特定条件满足或对共享资源进行保护,那么条件变量可能更适合

使用信号量需要进行加锁、解锁和等待操作,这可能引入一定的开销。尤其是在竞争激烈的场景下,频繁地获取和释放信号量可能导致性能下降

fcnt 给文件加读写锁,也可以用于进程间的同步

你可能感兴趣的:(linux,c++)