Linux 2.6.27后添加了一个新的特性,就是eventfd,是用来实现多进程或多线程的之间的事件通知的。
#include
int eventfd(unsigned int initval, int flags);
这个函数会创建一个事件对象(eventfd object),返回一个文件描述符,用来实现进程或线程间的等待/通知(wait/notify)机制。内核为这个对象维护了一个无符号的64位整形计数器 counter,用第一个参数(initval)初始化这个计数器,创建时一般可将其设为0,后面有例子测试这个参数产生的效果。
flags 可以使用三个宏:
1.我们可以看个父子进程间通信的例子,如下:
/*
* @filename: eventfd.c
* @author: Tanswer
* @date: 2018年01月08日 22:04:46
* @description:
*/
#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, i;
uint64_t u;
ssize_t rc;
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(i=1; iprintf("Child writing %s to efd\n",argv[i]);
u = atoll(argv[i]);
rc = write(efd, &u, sizeof(uint64_t));
if(rc != sizeof(uint64_t))
handle_error("write");
}
printf("Child completed write loop\n");
exit(EXIT_SUCCESS);
default:
sleep(2);
printf("Parent about to read\n");
rc = read(efd, &u, sizeof(uint64_t));
if(rc != sizeof(uint64_t))
handle_error("read");
printf("Parent read %llu from efd\n",(unsigned long long)u);
case -1:
handle_error("fork");
}
return 0;
}
父进程 sleep(2) ,保证子进程向 eventfd 连续写入, 然后父进程从 eventfd 中读取。
2.再看个线程间唤醒的例子
/*
* @filename: eventfd_pthread.c
* @author: Tanswer
* @date: 2018年01月08日 22:46:38
* @description:
*/
#include
#include
#include
#include
#include
int efd;
void *threadFunc()
{
uint64_t buffer;
int rc;
while(1){
rc = read(efd, &buffer, sizeof(buffer));
if(rc == 8){
printf("notify success\n");
}
printf("rc = %llu, buffer = %lu\n",(unsigned long long)rc, buffer);
}//end while
}
int main()
{
pthread_t tid;
int rc;
uint64_t buf = 1;
efd = eventfd(0,0); // blocking
if(efd == -1){
perror("eventfd");
}
//create thread
if(pthread_create(&tid, NULL, threadFunc, NULL) < 0){
perror("pthread_create");
}
while(1){
rc = write(efd, &buf, sizeof(buf));
if(rc != 8){
perror("write");
}
sleep(2);
}//end while
close(efd);
return 0;
}
下面我们改下 initval 参数,设为 3,即efd = eventfd(3,0)
,其他代码不变,运行程序结果如下:
可以看到我们改变initval 这个计数器的初始值,只会影响第一次读到的 buffer 的值,后面都还是 1。
下面我们把 main 函数的 buf 设为 0,即 counter 每次不变,initval 还是设为 3,看下。
uint64_t buf = 0;
efd = eventfd(3,0); // blocking
if(efd == -1){
perror("eventfd");
}
运行效果如下:
可以看到唤醒了一次,然后就一直阻塞了。
从上面可以看出来,eventfd 支持三种操作:read、write、close。
read 返回值的情况如下:
write :
可多次 write,一次 read。close 就是关掉 fd。
以上大概就是我了解的 eventfd,它相比于 pipe来说,少用了一个文件描述符,而且不必管理缓冲区,单纯的事件通知的话,方便很多(它的名字就叫做 eventfd),它可以和事件通知机制很好的融合。