#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
//这个函数会创建一个 事件对象 (eventfd object), 用来实现,进程(线程)间 的 等待/通知(wait/notify) 机制
//使用第一个参数(initval)初始化这个计数器。
//调用这个函数就会返回一个新的文件描述符(event object)。
//flags 取值:
//EFD_NONBLOCK 功能同open(2) 的O_NONBLOCK,设置对象为非阻塞状态,如果没有设置这个状态的话,read(2)读eventfd,并且计数器的值为0 就一直堵塞在read调用当中,要是设置了这个标志, 就会返回一个 EAGAIN 错误(errno = EAGAIN)。效果也如同 额外调用select(2)达到的效果。
//EFD_CLOEXEC 这个标识被设置的话,调用exec后会自动关闭文件描述符,防止泄漏。

另外两个相关的函数是read和write,请看下面的例子:

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>

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

int main( int argc, char **argv )
{
uint64_t u;
ssize_t s;
int j;
if ( argc < 2 ) {
fprintf(stderr, "input <num> in command argument\n");
exit(1);
}

int efd;
if ( (efd = eventfd(0, EFD_NONBLOCK)) == -1 )//如果EFD_NONBLOCK改为0,则read会阻塞等待
handle_error("eventfd failed\n");


switch (fork()) {
case 0:
for( j = 1; j < argc; j ++ ) {
printf("Child writing %s to efd\n", argv[j] );
sleep(2);
u = strtoull(argv[j], NULL, 0); /* analogesly atoi */
s = write(efd, &u, sizeof(uint64_t)); /* append u to counter */
if ( s != sizeof(uint64_t) )
handle_error("write efd failed\n");

}
printf("child completed write loop\n");

exit(0);
default:
//sleep (1);
re:
printf("parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if ( s != sizeof(uint64_t) ) {
if (errno = EAGAIN) {
printf("Parent read value %d\n", s);
return 1;
}
handle_error("parent read failed\n");
}
printf("parent read %d , %llu (0x%llx) from efd\n",
s, (unsigned long long)u, (unsigned long long) u);
goto re;
exit(0);

case -1:
handle_error("fork \n");
}
return 0;
}

这个例子比较简单,起了两个进程,父不停读,子根据传入参数写,父取出的是子所有写入的数字。

eventfd和epoll配合起来相当强大,感谢linux的一切都是文件设计,eventfd可以无缝纳入这个体系。下面是个联合应用的例子:

//gcc eventfd.c -o eventfd -lpthread
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdint.h>
#include <pthread.h>
#include <sys/eventfd.h>
#include <sys/epoll.h>

int efd = -1;

void *read_thread(void *dummy)
{
int ret = 0;
uint64_t count = 0;
int ep_fd = -1;
struct epoll_event events[10];

if (efd < 0)
{
printf("efd not inited.\n");
goto fail;
}

ep_fd = epoll_create(1024);
if (ep_fd < 0)
{
perror("epoll_create fail: ");
goto fail;
}


struct epoll_event read_event;

read_event.events = EPOLLHUP | EPOLLERR | EPOLLIN;
read_event.data.fd = efd;

ret = epoll_ctl(ep_fd, EPOLL_CTL_ADD, efd, &read_event);
if (ret < 0)
{
perror("epoll ctl failed:");
goto fail;
}


while (1)
{
ret = epoll_wait(ep_fd, &events[0], 10, 5000);
if (ret > 0)
{
int i = 0;
for (; i < ret; i++)
{
if (events[i].events & EPOLLHUP)
{
printf("epoll eventfd has epoll hup.\n");
goto fail;
}
else if (events[i].events & EPOLLERR)
{
printf("epoll eventfd has epoll error.\n");
goto fail;
}
else if (events[i].events & EPOLLIN)
{
int event_fd = events[i].data.fd;
ret = read(event_fd, &count, sizeof(count));
if (ret < 0)
{
perror("read fail:");
goto fail;
}
else
{
struct timeval tv;

gettimeofday(&tv, NULL);
printf("success read from efd, read %d bytes(%llu) at %lds %ldus\n",
ret, count, tv.tv_sec, tv.tv_usec);
}
}
}
}
else if (ret == 0)
{
/* time out */
printf("epoll wait timed out.\n");
break;
}
else
{
perror("epoll wait error:");
goto fail;
}
}

fail:
if (ep_fd >= 0)
{
close(ep_fd);
ep_fd = -1;
}

return NULL;
}

int main(int argc, char *argv[])
{
pthread_t pid = 0;
uint64_t count = 0;
int ret = 0;
int i = 0;

//efd = eventfd(0, 0);
printf("EFD_NONBLOCK:%d\n",EFD_NONBLOCK);
efd = eventfd(0, EFD_NONBLOCK);
if (efd < 0)
{
perror("eventfd failed.");
goto fail;
}

ret = pthread_create(&pid, NULL, read_thread, NULL);
if (ret < 0)
{
perror("pthread create:");
goto fail;
}

for (i = 0; i < 5; i++)
{
count = 4;
ret = write(efd, &count, sizeof(count));
if (ret < 0)
{
perror("write event fd fail:");
goto fail;
}
else
{
struct timeval tv;

gettimeofday(&tv, NULL);
printf("success write to efd, write %d bytes(%llu) at %lds %ldus\n",
ret, count, tv.tv_sec, tv.tv_usec);
}

sleep(1);
}

fail:
if (0 != pid)
{
pthread_join(pid, NULL);
pid = 0;
}

if (efd >= 0)
{
close(efd);
efd = -1;
}
return ret;
}