从Linux 2.6.27版本开始,新增了不少系统调用,其中包括eventfd,它的主要是用于进程或者线程间的通信(如通知/等待机制的实现)。
函数原型:
#include <sys/eventfd.h>
int eventfd(unsigned int initval, intflags);
参数解释:
如果是2.6.26或之前版本的内核,flags 必须设置为0。
Flags支持一下标志位:
EFD_NONBLOCK 类似于使用O_NONBLOCK标志设置文件描述符。
EFD_CLOEXEC 类似open以O_CLOEXEC标志打开, O_CLOEXEC 应该表示执行exec()时,之前通过open()打开的文件描述符会自动关闭测试时,在open()之后,调用一下exec(),在新的进程中检测描述符是否已经关闭
Initval:初始化计数器值,该值(暂时取名为A)保存在内核。
函数返回一个文件描述符,与打开的其他文件一样,可以进行读写操作。
Read:如果计数器A的值不为0时,读取成功,获得到该值。
如果A的值为0,非阻塞模式时,会直接返回失败,并把error置为EINVAL
如果为阻塞模式,一直会阻塞到A为非0为止。
Write:将缓冲区写入的8字节整形值加到内核计数器上,即会增加8字节的整数在计数器A上,如果A的值达到0xfffffffffffffffe时,就会阻塞(在阻塞模式下),直到A的值被read。阻塞和非阻塞情况同上面read一样。
#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);
}
}
运行结果:
./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时初始化计数器的值。