linux事件通知机制eventfd,Linux 系统调用 eventfd

本文详细介绍了Linux下的eventfd API,包括其功能、参数意义及应用场景,并通过一个父进程与子进程间通信的例子展示了如何使用eventfd实现进程间的等待/通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

eventfd 是linux特有的API,用于通知/等待机制的实现,该函数一般有两个使用场景:(1)用来实现用户态进程(线程)间的等待/通知(wait/notify) 机制

(2)内核用来通知用户态应用程序某个事件的发生。

第一种场景,本文会给一个示例程序进行说明;第二种场景,可以通过cgroup中的事件通知机制进行了解和学习。

eventfd 函数

该函数的原型为:#include

int eventfd(unsigned int initval, int flags);

这个函数会创建一个事件对象 (eventfd object), 用来实现进程(线程)间的等待/通知(wait/notify) 机制。 内核会为这个对象维护一个64位的计数器(uint64_t)。并且使用第一个参数(initval)初始化这个计数器counter,一般初始化设置为0。

调用这个函数就会返回一个新的文件描述符(event object)。2.6.27版本开始可以按位设置第二个参数(flags)。

第二个参数flags可以设置如下参数EFD_CLOECEX、EFD_NONBLOCK、EFD_SEMAPHORE。flags含义EFD_CLOEXEC为新创建的描述符设置FD_CLOEXEC flag

EFD_NONBLOCK为新创建的描述符设置O_NONBLOCK flag

EFD_SEMAPHOREsemaphore-like semantics for reads

后面会重点介绍一下EFD_SEMAPHORE,它决定创建的事件对象 (eventfd object)的读的行为,

read操作

如果计数值counter的值不为0,读取成功,获得到该值。如果counter的值为0,非阻塞模式,会直接返回失败,并把errno的值指纹EINVAL。如果为阻塞模式,一直会阻塞到counter为非0位置。如果没有设置 EFD_SEMAPHORE标记,当counter不为0时,read操作会返回counter的值,并将counter的值重置为0.

如果设置了EFD_SEMAPHORE标记,当counter不为0时,read操作会返回1,并将counter的值减少1.

write操作

write操作会增加8字节的整数在计数器counter上,如果counter的值达到0xfffffffffffffffe时,就会阻塞。直到counter的值被read。阻塞和非阻塞情况同上面read一样。

示例程序

以下示例程序通过父进程read操作,子进程write操作来演示eventfd的行为,同时我们可以使用选项-s来控制是否设置标记EFD_SEMAPHORE。该示例程序是根据man手册上的程序修改而来的。#define _GNU_SOURCE

#include

#include

#include

#include

#include /* Definitioin of uint64_t */

#define handle_err(msg)

do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void usage(char *pname) {

fprintf(stderr, "Usage: %s [options] ...n", pname);

fprintf(stderr, "Options can be:n");

fprintf(stderr, " -s semaphore-like semanticsn");

exit(EXIT_FAILURE);

}

int main(int argc, char **argv) {

intefd, j;

uint64_tu;

ssize_ts;

int flags = 0;

int opt;

while ((opt = getopt(argc, argv, "s")) != -1) {

switch (opt) {

case 's': flags |= EFD_SEMAPHORE;break;

default: usage(argv[0]);

}

}

if (optind >= argc)

usage(argv[0]);

efd = eventfd(0, flags);

if (efd == -1)

handle_err("eventfd");

switch (fork()) {

case -1:

handle_err("fork");

case 0: /*child */

for (j = optind; j < argc; j++) {

printf("Child writing %s to efdn", argv[j]);

u = strtoull(argv[j], NULL, 0);

s = write(efd, &u, sizeof(uint64_t));

if (s != sizeof(uint64_t))

handle_err("write");

sleep(1);

}

printf("Child completed write loopn");

exit(EXIT_SUCCESS);

default: /* parent */

while (1) {

s = read(efd, &u, sizeof(uint64_t));

if (s != sizeof(uint64_t))

handle_err("read");

printf("Parent read %llu (0x%llx) from efdn",

(unsigned long long)u, (unsigned long long)u);

sleep(2);

}

}

}

未指定EFD_SEMAPHORE标记时:# ./test_eventfd 1 2 3 4 5

Child writing 1 to efd

Parent read 1 (0x1) from efd

Child writing 2 to efd

Child writing 3 to efd

Parent read 2 (0x2) from efd

Child writing 4 to efd

Parent read 7 (0x7) from efd

Child writing 5 to efd

Child completed write loop

Parent read 5 (0x5) from efd

可以看出,每次read时都读取了counter的当前值,并将counter清0.

指定EFD_SEMAPHORE标记时:# ./test_eventfd -s 1 2 3 4 5

Child writing 1 to efd

Parent read 1 (0x1) from efd

Child writing 2 to efd

Child writing 3 to efd

Parent read 1 (0x1) from efd

Child writing 4 to efd

Parent read 1 (0x1) from efd

Child writing 5 to efd

Child completed write loop

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

Parent read 1 (0x1) from efd

可以看出,每次读取到的值为1,只要counter不为0,就可以一直读取。

参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值