- 专栏内容:linux下并发编程
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
概述
原理简介
使用场景
接口说明
头文件
参数说明
代码演示
默认参数
信号量模式
结尾
前言
本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。
概述
eventfd 就是一个用于事件通知的fd。当然linux中,一切都可以做为文件来看待,所以就有fd。这样有一个好处管理统一,比如可以加入到epoll事件等待中。很多人可能没怎么用,但是用过的人都说:香 !
原理简介
eventfd 提供了一个进程/线程间通信的方式。
这个方式是载体是一个句柄,也就是fd,类型是eventfd,可以在/proc下查看fd时看到;
通过它可以传递事件信息,事件就是write次数累计,这个累计值用一个8字节整型值来记录,每次write时这个整型值自动会累计,它是由内核来维护,read时就会拿到累计值,并清零fd中的值。
当然这个eventfd,可以传入poll来监听,监听可读可写事件。
可写事件, eventfd的write是一直可以的,它可以不断累加,所以一直会是可写状态,所以可以不用理会可写事件。
可读事件,当eventfd的累计值为零时,为不可读状态,当大于零时,才可读。这样就提供了一种通知机制。
使用场景
适用于等待-通知的架构模式,类似于信号量的场景。
比如生产者准备好后,通知消费者;等待的消费者获取到通知后,进行消费;消费完后,消费者又开始等待。
那么,信号量也可以实现,与eventfd有什么区别呢?
两者都是内核变量,eventfd的优势在于,它可以作为文件fd来读写,同时还可以使用poll/select实现异步等待,也就是说eventfd在等待时,你还可以干别的事,如果有可读时,你再去读;而信号量则不行,只能是等待或者不等待。
接口说明
头文件
#include <sys/eventfd.h>
创建eventfd类型的句柄
int eventfd(unsigned int initval, int flags);
参数说明
initval | 计数的初值 |
flags | EFD_CLOEXEC: 在调用exec创建进程时会自动关闭fd。 EFD_NONBLOCK: 创建非阻塞模式的fd,也就是在计数为0时,不会等待,直接返回-1;不指定时,默认为阻塞模式。 EFD_SEMAPHORE: 创建类似信号量的模式,如果计数大于0时,每次读到的是1,同时计数自动减1,到0时阻塞。 不指定时,每次读全部累计值,计数被重置为0。 |
代码演示
默认参数
先看一个默认参数,默认是阻塞模式,每次都会取累计值,并重置eventfd中的累计值为零;如果累计值为0,则会阻塞。
#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */
int main(int argc, char *argv[])
{
int efd;
uint64_t u = 0;
ssize_t s;
efd = eventfd(0, 0);
if (efd == -1)
return -1;
u = 0x01;
printf("write eventfd %llu \n", u);
s = write(efd, &u, sizeof(uint64_t));
u = 0x02;
printf("write eventfd %llu \n", u);
s = write(efd, &u, sizeof(uint64_t));
u = 0x03;
printf("write eventfd %llu \n", u);
s = write(efd, &u, sizeof(uint64_t));
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
{
printf("read failure.\n");
}
printf("read %llu from efd\n", u);
close(efd);
return 0;
}
信号量模式
再来看一下使用信号量模式的效果,虽然写入了几次,但是每次read只读出1,累计值也是每次都会减1;
#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */
int main(int argc, char *argv[])
{
int efd;
uint64_t u = 0;
ssize_t s;
efd = eventfd(0, EFD_SEMAPHORE);
if (efd == -1)
return -1;
u = 0x01;
printf("write eventfd %llu \n", u);
s = write(efd, &u, sizeof(uint64_t));
u = 0x02;
printf("write eventfd %llu \n", u);
s = write(efd, &u, sizeof(uint64_t));
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
{
printf("read failure.\n");
}
printf("read %llu from efd\n", u);
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
{
printf("read failure.\n");
}
printf("read %llu from efd\n", u);
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
{
printf("read failure.\n");
}
printf("read %llu from efd\n", u);
close(efd);
return 0;
}
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。
注:未经同意,不得转载!