eventfd
eventfd 在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r 。
- 原型
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
/**
* initval: 创建eventfd时它所对应的64位计数器的初始值,可以写0
* flags: 是2.6.26或之前版本的内核,flags 必须设置为0
* EFD_CLOEXEC表示返回的eventfd文件描述符在fork后exec其他程序时会自动关闭这个文件描述符;
* EFD_NONBLOCK设置返回的eventfd非阻塞;
* EFD_SEMAPHORE表示将eventfd作为一个信号量来使用。
*
*eventfd设置了EFD_SEMAPHORE,那么每次read就会返回1,并且让eventfd对应的计数器减一;
*如果eventfd没有设置EFD_SEMAPHORE,那么每次read就会直接返回计数器中的数值,read之后计数器就会置0。不管是哪一种,当计数器为0时,如果继续read,那么read就会阻塞(如果eventfd没有设置EFD_NONBLOCK)或者返回EAGAIN错误(如果eventfd设置了EFD_NONBLOCK)。
*/
efd = eventfd(0, 0);
if (efd == -1)
handle_error("eventfd");
-
作用
- eventfd顾名思义就是事件fd类型,就是专门用于事件同志的文件描述符(fd)
- eventfd 是一个计数相关的fd,计数不为零是有可读事件发生 write 仅仅是加计数,read 是读计数,并且清零
-
eventfd 结合epoll
eventfd 是专门用来传递事件的 fd ,而 epoll 池则是专门用来管理事件的池子,它们两结合就妙了
eventfd 可以和 libaio & epoll 一起,实现 Linux 下的纯异步 IO
ext4 这种文件 fd 一直可读可写,所以实现 poll 毫无意义。eventfd 一直可写,所以监听可写毫无意义;
eventfd 可以结合业务,做一个事件通知的通信机制,非常巧妙; -
子线程多次写入多个值,主线程一次读出所有值的和
#include <iostream>
#include <sys/eventfd.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
using namespace std;
int main(int argc,char *argv[])
{
int efd,j;
uint64_t u;
ssize_t s;
if(argc < 2)
{
printf("number of argc is wrong!\n");
return 0;
}
efd = eventfd(0,0);
if( -1 == efd )
printf("failed to create eventfd\n");
switch (fork()) {
case 0:
for(j=1;j<argc;j++)
{
printf("child writing %s to efd\n",argv[j]);
u = strtoul(argv[j],NULL,0);
s = write(efd,&u,sizeof(uint64_t));
if(s!=sizeof(uint64_t))
printf("write efd failed\n");
}
printf("Child completed write loop\n");
exit(0);
break;
default:
sleep(2);
printf("Parents about to read\n");
s = read(efd,&u,sizeof(uint64_t));
if(s != sizeof(uint64_t))
printf("read efd failed\n");
printf("Parents first read %llu (0x%llu) from efd\n",u,u);
exit(0);
case -1:
printf("fork error\n");
}
return 0;
}
如果没有写入操作,但是并没有导致初始值变化,则主线程会一直挂在read操作上

- eventfd可以被epoll监控, 一旦有状态变化,可以触发通知
#include <iostream>
#include <sys/eventfd.h>
#include <sys/epoll.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int g_iEvtfd = -1;
void * pthread_cback(void *arg)
{
uint64_t uiWrite = 1;
while(1)
{
sleep(2);
if(0 != eventfd_write(g_iEvtfd,uiWrite))
{
printf("child write iEvtfd failed\n");
}
}
pthread_exit(NULL);
}
int main()
{
int iEvtfd,j;
uint64_t uiWrite = 1;
uint64_t uiRead;
ssize_t s;
int iEpfd;
struct epoll_event stEvent;
int iRet = 0;
struct epoll_event stEpEvent;
pthread_t stWthread;
iEpfd = epoll_create(1);
if( -1 == iEpfd )
{
printf("Create epoll failed.\n");
return 0;
}
iEvtfd = eventfd(0,0);
if (-1 == iEvtfd)
{
printf("failed to create eventfd\n");
return 0;
}
g_iEvtfd = iEvtfd;
memset(&stEvent,0,sizeof(struct epoll_event));
stEvent.events = (unsigned long) EPOLLIN;
stEvent.data.fd = iEvtfd;
iRet = epoll_ctl(iEpfd,EPOLL_CTL_ADD,g_iEvtfd,&stEvent);
if(0 != iRet)
{
printf("failed to add iEvtfd to epoll\n");
close(g_iEvtfd);
close(iEpfd);
return 0;
}
iRet = pthread_create(&stWthread,NULL,pthread_cback,NULL);
if(0 != iRet)
{
close(g_iEvtfd);
close(iEpfd);
return 0;
}
for(;;)
{
iRet = epoll_wait(iEpfd,&stEpEvent,1,-1);
if(iRet > 0)
{
s = eventfd_read(iEvtfd,&uiRead);
if(s != 0)
{
printf("read iEvtfd failed\n");
break;
}
printf("Read %llu (0x%llx) from iEvtfd\n",uiRead,uiRead);
}
}
close(g_iEvtfd);
close(iEpfd);
return 0;
}

浙公网安备 33010602011771号