0

0

Linux中的神器:eventfd的原理与应用

王林

王林

发布时间:2024-02-13 20:30:16

|

1039人浏览过

|

来源于良许Linux教程网

转载

linux是一个强大的操作系统,它提供了许多高效的进程间通信机制,如管道、信号、消息队列、共享内存等。但是,有没有一种更简单、更灵活、更高效的通信方式呢?答案是有的,那就是eventfd。eventfd是linux 2.6版本引入的一种系统调用,它可以用来实现事件通知,也就是通过一个文件描述符来传递事件。eventfd包含一个由内核维护的64位无符号整型计数器,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值,从而实现进程间通信。eventfd有什么优点呢?它有以下几个特点:

Linux中的神器:eventfd的原理与应用

  • eventfd不需要创建任何额外的文件或者内存空间,只需要一个文件描述符即可;
  • eventfd可以与select、poll、epoll等多路复用机制结合使用,实现高效的事件驱动编程;
  • eventfd可以设置为非阻塞或者信号量模式,提供了不同的通信语义;
  • eventfd可以跨越进程或者线程边界,实现不同级别的通信。

那么,eventfd是如何工作的呢?它又有哪些应用场景呢?本文将从原理和应用两个方面来介绍eventfd这个神器。

一般来说:Linux进程间通信有五大方案:管道,消息队列,信号量,共享内存,套接字。
管道我不是很熟,只了解一般管道局限与父子进程之间,首先就被我排除了,因为我要做的是相互独立的进程间通信,命名管道似乎不局限于父子进程,但在内核态怎么使用不清楚。
消息队列完全不了解。
信号量的核心是一个内核变量的原子操作,但接口只体现在用户态,而且信号量的P V操作更多做的好像是互斥,而不是我想要的通知唤醒机制。
共享内存就更麻烦了,接口只在用户态,如果自己想做内核态与用户态之间的共享内存,得自己写file,然后提供mmap接口。
套接字之前只是用过af_inet的tcp/udp与af_unix的dgram,还是上面的那个问题,内核没有明确的接口提供,虽然可以自己去用比如sock->ops->recvmsg这样的函数去调用,但毕竟需要自己构造入参,感觉还是不太安全。

那么剩下的似乎只有netlink了,这个socket明确地提供了内核的发包函数,因为它明确地export出了netlink_kernel_create函数,所以内核态的函数得以用这个sock来进行发包。但是一个是用户态需要注册收包函数,另一个内核态发包还是免不了要组装skb,对于我单纯地只想进行通知唤醒来说还是过于复杂了。

于是我再次寻找,发现了eventfd这个神器,在KVM与Qemu的通信之间,eventfd被大牛使用的出神入化,仔细地分析了一下源码,发现这个东西就如名字所说,纯是为了通知而存在的。
作为一个file(linux里有不是file的东西么~~),它的private_data结构体 eventfd_ctx只有可怜的四个变量。

struct eventfd_ctx {
  struct kref kref;  /* 这个就不多说了,file计数用的,用于get/put */
  wait_queue_head_t wqh; /* 这个用来存放用户态的进程wait项,有了它通知机制才成为可能 */
/*
\* Every time that a write(2) is performed on an eventfd, the
\* value of the __u64 being written is added to "count" and a
\* wakeup is performed on "wqh". A read(2) will return the "count"
\* value to userspace, and will reset "count" to zero. The kernel
\* side eventfd_signal() also, adds to the "count" counter and
\* issue a wakeup.
*/
  __u64 count;  /* 这个就是一个技术器,应用程序可以自己看着办,read就是取出然后清空,write就是把value加上 */
  unsigned int flags;  /* 所有的file都有的吧,用来存放阻塞/非阻塞标识或是O_CLOEXEC之类的东西 */
};
  我之所以选用它是因为它有 eventfd_signal 这个特地为内核态提供的接口,下面的是注释。
 \* This function is supposed to be called by the kernel in paths that do not
 \* allow sleeping. In this function we allow the counter to reach the ULLONG_MAX
 \* value, and we signal this as overflow condition by returining a POLLERR to poll(2).

其实看代码会更清晰一些

ECTouch移动商城系统
ECTouch移动商城系统

ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有

下载
int eventfd_signal(struct eventfd_ctx *ctx, int n)
{
  unsigned long flags;

  if (n return -EINVAL;
  spin_lock_irqsave(&ctx->wqh.lock, flags);
  if (ULLONG_MAX - ctx->count count);
  ctx->count += n;
  if (waitqueue_active(&ctx->wqh))
    wake_up_locked_poll(&ctx->wqh, POLLIN);
  spin_unlock_irqrestore(&ctx->wqh.lock, flags);

  return n;
}  

本质就是做一次唤醒,不用read,也不用write,与eventfd_write的区别是不用阻塞

下面说一下我的具体用法:
内核态是一个模块,注册一个misc设备,创建内核线程工作(参数为模块的file->private_data)。提供ioctl接口供用户态进程下发自己eventfd创建的fd,保存在内核线程可以访问到的file->private_data中。
当内核态想通知用户态时,直接使用eventfd_signal,此时用户态线程需要先把自己放在eventfd_ctx->wqh上,有两种方案,一个是调用read,一个是调用poll。 如果是read,之后会将eventfd_ctx->count清零,下次还能阻塞住。但是如果使用poll,之后count并未清零,导致再次poll时,即使内核态没有eventfd_signal,poll也会即时返回。
用户态通知内核态稍微麻烦一点,,首先需要再创建一个eventfd,然后下发给file->private_data(这里的操作同上面),额外需要在模块里做一个iotcl,专门负责用户态来通知内核态,函数里就做eventfd_signal,内核态线程需要先放在eventfd_ctx->wqh上,可以利用vfs_read,或者自己在内核态做一次poll(似乎又麻烦了)。

本文介绍了eventfd这个Linux中的神器,它是一种简单、灵活、高效的进程间通信机制。我们从原理方面分析了eventfd的创建、读写和标志位等内容,并且给出了相应的代码示例。我们还从应用方面介绍了eventfd在用户态与内核态通信、定时器和事件触发器等场景中的使用方法,并且给出了相应的代码示例。通过本文的学习,我们可以掌握eventfd的基本用法,并且能够在实际开发中灵活地运用eventfd来实现不同的通信需求。希望本文对你有所帮助!

相关专题

更多
counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

193

2023.11.20

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

193

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2025.12.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

472

2023.08.10

tcp和udp的区别
tcp和udp的区别

TCP和UDP的区别,在连接性、可靠性、速度和效率、数据报大小以及适用场景等方面。本专题为大家提供tcp和udp的区别的相关的文章、下载、课程内容,供大家免费下载体验。

116

2023.07.25

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

65

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 7.7万人学习

Rust 教程
Rust 教程

共28课时 | 4万人学习

Vue 教程
Vue 教程

共42课时 | 5.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号