linux进程通信eventfd
前言
在muduo网络应用框架中EventLoop类中学习到关于eventfd,所以系统性查找资料学习有关eventfd。
知识点
eventfd通过一个进程间共享的64位计数器来实现进程间的通信,基于事件驱动实现的轻量级进程间通信的系统调用。
int eventfd(unsigned int initval, int flags);
# 返回一个新的文件描述符,该描述符可以用于引用eventfd对象。
- initval:初始化计数器的值。
- flags:有三个取值。(在2.6.26版本以下的Linux,flags未使用情况下必须为0)
- EFD_CLOEXEC:在执行fork子进程的时候一般情况会把父进程的文件描述符复制,而使用该参数可以避免这个问题。
- EFD_NONBLOCK:设置非阻塞。如果没有该参数read操作会一直阻塞到计数器中有值,相反直接返回-1。
- EFD_SEMAPHORE:在Linux2.6.30内核后支持,每次read计数器会减一。
read:读取成功返回8字节int型整数,如果缓冲区大小小于8字节则返回EINVAL错误。
write:将缓冲区写入8字节int型整数累加到计数器中,可以存储在计数器中的最大值是最大的无符号64位值减去1(即0xfffffffffffffffe)
结合EPOLL实现通信
#include <iostream>
#include <sys/eventfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#define MAXEVENTS 1024
#define EPOLLWAIT_TIME 10000
int createEventfd() {
int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evtfd < 0) {
abort();
}
return evtfd;
}
int main(int argc, char *argv[]) {
int evtfd = createEventfd();
int epollfd = epoll_create(MAXEVENTS);
struct epoll_event event;
event.data.fd = evtfd;
// 监听可读事件&ET边缘触发
event.events = EPOLLIN | EPOLLET;
int ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evtfd, &event);
if (ret < 0) {
std::cout << "create epoll add event fail" << std::endl;
abort();
}
// fork 子进程
int pid = fork();
// 父进程
if (pid > 0) {
struct epoll_event events[MAXEVENTS];
// 监听事件发生
int nfds = epoll_wait(epollfd, events, MAXEVENTS, EPOLLWAIT_TIME);
for (int i = 0; i < nfds; ++i) {
if ((events[i].data.fd == evtfd) && (events[0].events & EPOLLIN)) {
eventfd_t res;
read(events[i].data.fd, &res, sizeof(eventfd_t));
std::cout << "parent read is: " << res << std::endl;
}
}
close(evtfd);
close(epollfd);
}
// 子进程
else if (pid == 0) {
std::cout << "child sleep 3s" << std::endl;
sleep(3);
//向eventfd计数器写入
eventfd_t num = 2;
if (write(evtfd, &num, sizeof(eventfd_t)) == sizeof(eventfd_t)) {
std::cout << "child write succ" << std::endl;
}
close(evtfd);
}
return 0;
}