eventfd是linux 2.6.22后系统提供的一个轻量级的进程间通信的系统调用,eventfd通过一个进程间共享的64位计数器完成进程间通信,这个计数器由在linux内核空间维护,用户可以通过调用write方法向内核空间写入一个64位的值,也可以调用read方法读取这个值。

新建

创建一个eventfd对象,或者说打开一个eventfd的文件,类似普通文件的open操作。

该对象是一个内核维护的无符号的64位整型计数器。初始化为initval的值。

#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);

flags可以以下三个标志位的OR结果:

  • EFD_CLOEXEC : fork子进程时不继承,对于多线程的程序设上这个值不会有错的。
  • EFD_NONBLOCK: 文件会被设置成O_NONBLOCK,读操作不阻塞。若不设置,一直阻塞直到计数器中的值大于0。
  • EFD_SEMAPHORE : 支持 semophore 语义的read,每次读操作,计数器的值自减1。

读操作

读取计数器中的值。

typedef uint64_t eventfd_t;
int eventfd_read(int fd, eventfd_t *value);
  1. 如果计数器中的值大于0:
  • 设置了 EFD_SEMAPHORE 标志位,则返回1,且计数器中的值也减去1。
  • 没有设置 EFD_SEMAPHORE 标志位,则返回计数器中的值,且计数器置0。
  1. 如果计数器中的值为0:
  • 设置了 EFD_NONBLOCK 标志位就直接返回-1。
  • 没有设置 EFD_NONBLOCK 标志位就会一直阻塞直到计数器中的值大于0。

写操作

向计数器中写入值。

int eventfd_write(int fd, eventfd_t value);
  1. 如果写入值的和小于0xFFFFFFFFFFFFFFFE,则写入成功

  2. 如果写入值的和大于0xFFFFFFFFFFFFFFFE

  • 设置了 EFD_NONBLOCK 标志位就直接返回-1。
  • 如果没有设置 EFD_NONBLOCK 标志位,则会一直阻塞直到read操作执行

关闭

#include <unistd.h>
int close(int fd);

示例

示例1-一读一写

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream> int main() {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
eventfd_write(efd, 2);
eventfd_t count;
eventfd_read(efd, &count);
std::cout << count << std::endl;
close(efd);
}

上述程序主要做了如下事情:

  • 创建事件,初始计数器为0;
  • 写入计数2;
  • 读出计数2
  • 关闭事件

示例2-多读多写

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream> int main() {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
eventfd_write(efd, 2); // 写入2,计数器为2
eventfd_write(efd, 3); // 写入3, 计数器为2 + 3 = 5
eventfd_write(efd, 4); // 写入3, 计数器为5 + 4 = 9
eventfd_t count;
int read_result = eventfd_read(efd, &count);
std::cout << "read_result=" << read_result << std::endl; // 0
std::cout << "count=" << count << std::endl; // count = 9
read_result = eventfd_read(efd, &count);
std::cout << "read_result=" << read_result << std::endl; // -1,返回失败
std::cout << "count=" << count << std::endl; // count = 9,为原来的值
close(efd);
}

示例3-EFD_SEMAPHORE标志位的作用:

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream> int main() {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
eventfd_write(efd, 2); // 写入2,计数器为2
eventfd_t count;
int read_result = eventfd_read(efd, &count); // count = 1,计数器自减1,为1
std::cout << "read_result=" << read_result << std::endl; // 0
std::cout << "count=" << count << std::endl; // 1
read_result = eventfd_read(efd, &count); // count = 1,计数器自减1,为0
std::cout << "read_result=" << read_result << std::endl; // 0
std::cout << "count=" << count << std::endl; // 1
read_result = eventfd_read(efd, &count); // 读取失败
std::cout << "read_result=" << read_result << std::endl; // -1,读取失败
std::cout << "count=" << count << std::endl; // 1
close(efd);
}

可以看到设置了EFD_SEMAPHORE后,每次读取到的值都是1,且read后计数器也递减1。

参考

微信公共号

NFVschool,关注最前沿的网络技术。

原文阅读

Linux进程间通信-eventfd的更多相关文章

  1. Linux进程间通信(一): 信号 signal()、sigaction()

    一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中 ...

  2. Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

    我们已经知道,我们可以通过信号来终止进程,也可以通过信号来在进程间进行通信,程序也可以通过指定信号的关联处理函数来改变信号的默认处理方式,也可以屏蔽某些信号,使其不能传递给进程.那么我们应该如何设定我 ...

  3. Linux进程间通信(三):匿名管道 popen()、pclose()、pipe()、close()、dup()、dup2()

    在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值.这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据. 一.什 ...

  4. Linux进程间通信(四):命名管道 mkfifo()、open()、read()、close()

    在前一篇文章—— Linux进程间通信 -- 使用匿名管道 中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关 ...

  5. Linux进程间通信(五):信号量 semget()、semop()、semctl()

    这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信 -- 信号.下面 ...

  6. Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

    下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式 ...

  7. Linux进程间通信(七):消息队列 msgget()、msgsend()、msgrcv()、msgctl()

    下面来说说如何用不用消息队列来进行进程间的通信,消息队列与命名管道有很多相似之处.有关命名管道的更多内容可以参阅我的另一篇文章:Linux进程间通信 -- 使用命名管道 一.什么是消息队列 消息队列提 ...

  8. Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

    前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套 ...

  9. [转]Linux进程间通信——使用消息队列

    点击此处阅读原文 另收藏作者ljianhui的专栏初学Linux 下面来说说如何使用消息队列来进行进程间的通信,消息队列与命名管道有很多相似之处.有关命名管道的更多内容可以参阅我的另一篇文章:Linu ...

随机推荐

  1. 密码子演化假说|凝固事件假说|立体化学假说|共进化假说|代谢途径相关性假说|四重兼并|假四重兼并|最小损伤原则|AU-rich|GC-rich|逐步进化假说|分子机制进化假说

    生命组学 将密码子表重排后发现,嘌呤嘧啶含量不同,密码子的氨基酸种类由第一二位决定,同时第三位变化大却没有蛋白质层面上实质性的改变,这说明第三位氨基酸是用于维持氨基酸组成不发生变化同时保证蛋白质稳定性 ...

  2. basecalling|vector mark|Assembly的难题|

    生物信息学 染色体可以据染色图谱判断染色体号码,1-22号染色体依次变短,它们影响机体发育,23号染色体决定性别.肿瘤是由于遗传密码变异造成的.因此,遗传密码的解读非常重要,但是因为遗传密码长度非常长 ...

  3. 转:zabbix 2.0.6监控cisco交换机 2950 2960s 3560G

    转自: http://blog.chinaunix.net/uid-24250828-id-3806551.html 想在zabbix 上监控交换机端口的流量,找了两天的模板,包括官方的和网友写的.在 ...

  4. html解析过程

    Web页面运行在各种各样的浏览器当中,浏览器载入.渲染页面的速度直接影响着用户体验 简单地说,页面渲染就是浏览器将html代码根据CSS定义的规则显示在浏览器窗口中的这个过程.先来大致了解一下浏览器都 ...

  5. C++扬帆远航——2

    /* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:test.cpp * 作者:常轩 * 完成日期:2016年3月6 ...

  6. SpringBoot图文教程8 — SpringBoot集成MBG「代码生成器」

    有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1「概念+ ...

  7. 前端JS题

    题目如下: function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function ( ...

  8. PKI详解

    预备: 一.密码基元 二.密钥管理 三.PKI本质是把非对称密钥管理标准化 PKI 是 Public Key Infrastructure 的缩写,其主要功能是绑定证书持有者的身份和相关的密钥对(通过 ...

  9. 46-Python深浅拷贝

    目录 一.引言 1.1 可变或不可变 二.拷贝 三.浅拷贝 深拷贝 一.引言 在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象 ...

  10. SpringBoot&Shiro实现用户认证

    SpringBoot&Shiro实现用户认证 实现思路 思路:实现认证功能主要可以归纳为3点 1.定义一个ShiroConfig配置类,配置 SecurityManager Bean , Se ...