eventfd
在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r
。
1 2 |
#include<sys/eventfd.h> int eventfd(unsigned int initval,int flags); |
这个函数会创建一个 事件对象 (eventfd object), 用来实现,进程(线程)间的等待/通知(wait/notify) 机制. 内核会为这个对象维护一个64位的计数器(
并且使用第一个参数(
有如下的一些宏可以使用:
uint64_t
)。并且使用第一个参数(
initval
)初始化这个计数器。调用这个函数就会返回一个新的文件描述符(event object)。2.6.27版本开始可以按位设置第二个参数(flags
)。有如下的一些宏可以使用:
EFD_NONBLOCK
, 功能同open(2)
的O_NONBLOCK
,设置对象为非阻塞状态,如果没有设置这个状态的话,read(2)
读eventfd
,并且计数器的值为0 就一直堵塞在read
调用当中,要是设置了这个标志, 就会返回一个 EAGAIN 错误(errno = EAGAIN
)。效果也如同 额外调用select(2)
达到的效果。EFD_CLOEXEC
这个标识被设置的话,调用exec后子进程得不到这个句柄,而不影响fork
产生的子进程,更多的是安全方面的考虑。EFD_SEMAPHORE
,这个标识(since Linux 2.6.30)开始,但是在Android的NDK中是没有这个定义的,因此不建议在Android中使用。他的功能完全可以用默认参数替换,而且更高效,因为如果是信号量模式,每次调用,只会减少1,导致重复进入内核,性能实际上是有影响的。
如果是2.6.26或之前版本的内核,flags 必须设置为0。
创建这个对象后,可以对其做如下操作。
write
将缓冲区写入的8字节整形值加到内核计数器上。
read
读取8字节值, 并把计数器重设为0. 如果调用read
的时候计数器为0, 要是eventfd
是阻塞的, read
就一直阻塞在这里,否则就得到 一个EAGAIN
错误。
如果buffer的长度小于8那么read
会失败, 错误代码被设置成 EINVAL
。
poll
select
epoll
close
当不需要eventfd
的时候可以调用close
关闭, 当这个对象的所有句柄都被关闭的时候,内核会释放资源。 为什么不是close
就直接释放呢, 如果调用fork
创建
进程的时候会复制这个句柄到新的进程,并继承所有的状态。
程序实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <sys/eventfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { uint64_t u; int efd = eventfd(10, 0); if (efd == -1) handle_error("eventfd"); int ret = fork(); if(ret == 0) { for (int j = 1; j < argc; j++) { printf("Child writing %s to efd\n", argv[j]); u = atoll(argv[j]); ssize_t s = write(efd, &u, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("write"); } printf("Child completed write loop\n"); exit(EXIT_SUCCESS); } else { sleep(2); ssize_t s = read(efd, &u, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("read"); printf("Parent read %llu from efd\n",(unsigned long long)u); exit(EXIT_SUCCESS); } } |
运行结果:
比较简单,不做过解释。子进程写入命令行中传入的参数,父进程读取其中计数器的值。
运行结果:
1 2 3 4 5 6 |
./eventfd 10 20 30 Child writing 10 to efd Child writing 20 to efd Child writing 30 to efd Child completed write loop Parent read 70 from efd |
命令行传入的是10、20、30其和应为60,为啥读取的是70呢?请看15行调用eventfd时第一个参数是10,这个参数是创建eventfd时初始化计数器的值。
参考链接:Linux中eventfd函数调用解析