1.
#include <sys/eventfd.h>
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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <unistd.h>
/* 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 <num>...\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 <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;
}
用于在进程间传递事件信号,与 pipe 相比,eventfd 具有更高的性能和更好的可扩展性,可以支持更多的并发连接。
eventfd不支持非亲缘关系进程之间的事件通知,因为 eventfd
使用的内核数据结构属于文件描述符表,而文件描述符表只能在同一进程内共享。
无名信号量也不支持非亲缘关系的进程
eventfd 和 条件变量.和信号量都差不多, 都是可以通知唤醒,
.如果只是简单的事件通知或计数,且不需要对共享资源进行保护,那么 eventfd
可能更为高效
如果需要等待特定条件满足或对共享资源进行保护,那么条件变量可能更适合
使用信号量需要进行加锁、解锁和等待操作,这可能引入一定的开销。尤其是在竞争激烈的场景下,频繁地获取和释放信号量可能导致性能下降
fcnt 给文件加读写锁,也可以用于进程间的同步