news 2026/7/7 2:16:38

异步通知教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
异步通知教程

介绍

在Linux系统编程中,异步通知(Asynchronous Notification,也称信号驱动I/O)是一种让驱动程序主动“敲门”通知应用程序的通信机制。

简单来说,它不是让应用程序去“询问”设备是否有数据,而是让设备在数据就绪时“主动告诉”应用程序:“数据来了,快来取!”——这种模式非常类似于硬件中的中断

从广义理论层面来看,异步通知就是一种软件中断,触发标志就是kill_fasync()函数

如何使用

在linux系统编程中,想要使用异步通知涉及应用程序和驱动程序两端需要协同完成的步骤,下面是各自需要执行的具体操作及其作用。

应用程序端操作
  1. 编写并注册信号处理函数:使用signal()sigaction()系统调用,将SIGIO信号与一个自定义的处理函数绑定。

    • 作用:当驱动发来信号时,内核会自动调用此函数,应用程序在此函数中执行readwrite等操作来响应设备事件。

  2. 打开设备文件:使用open()系统调用打开目标设备的文件描述符。

    • 作用:获取一个文件描述符,它是后续所有操作的基础,也明确了要接收哪个设备的通知。

  3. 设置设备文件所有者:使用fcntl(fd, F_SETOWN, getpid()),将当前进程的PID设置为该设备文件的“所有者”。

    • 作用:告诉驱动程序信号应该发给哪个进程。这是实现精准通知的关键一步。

  4. 使能设备的异步通知模式:使用fcntl(fd, F_SETFL, flags | FASYNC),为设备文件描述符开启FASYNC标志。

    • 作用:向驱动程序“订阅”异步通知服务,表示“我已准备好接收你的信号了”。执行这一步会触发驱动中fasync()函数的调用。

// 1. 注册SIGIO信号处理函数 signal(SIGIO, sigio_signal_func); //当接收到这个信号时,运行这个sigio_signal_func函数,类似与中断函数 // 2. 设置进程归属:告诉内核当前进程接收SIGIO信号 fcntl(fd, F_SETOWN, getpid()); //设置设备文件拥有者为当前进程 // 3. 开启异步通知功能 flags = fcntl(fd, F_GETFL); //获取当前文件状态标志 fcntl(fd, F_SETFL, flags | FASYNC); //设置FASYNC标志,触发驱动fasync()
驱动程序端操作

驱动程序需要完成“提供服务”和“主动通知”两项核心功能。

  1. 定义异步通知结构体指针:在设备的结构体中,增加一个struct fasync_struct *类型的指针。

    • 作用:这个指针是驱动管理异步通知请求的核心,它指向一个用于维护所有“订阅者”(即应用程序)信息的数据结构。

  2. 实现fasync()文件操作:在驱动的file_operations结构中实现.fasync函数,其主体是调用内核函数fasync_helper()

    • 作用fasync_helper()负责将当前应用程序的fd等信息注册到第一步定义的fasync_struct队列中。它相当于驱动端的“订阅管理”接口。

  3. 在合适的时机发送信号:在设备资源就绪时(如中断处理函数中),调用内核函数kill_fasync()

    • 作用:遍历fasync_struct队列,向所有“订阅”了该设备事件的应用程序发送SIGIO信号。这是实现主动通知的最终动作。

    • 参数说明kill_fasyncband参数用于指明事件类型,可读时传POLL_IN,可写时传POLL_OUT

  4. (可选)在release()中清理:在驱动的release函数中,调用一次fasync_helper()

    • 作用:当设备文件被关闭时,自动将对应的应用程序从通知队列中移除,避免出现“野指针”或通知已失效的进程。

函数列表

1. 用户程序(应用程序端)核心函数
函数函数原型参数参数作用函数作用
signal()sighandler_t signal(int signum, sighandler_t handler);signum:信号编号handler:信号处理函数指针signum:指定要捕获的信号,异步通知中使用SIGIOhandler:可为SIG_IGN(忽略)、SIG_DFL(默认处理)或用户自定义函数指针注册信号处理函数,将指定信号与处理函数绑定。当驱动发送SIGIO信号时,内核自动调用此函数
sigaction()int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);signum:信号编号act:新行为配置指针oldact:旧行为保存指针signum:指定要捕获的信号act:指向sigaction结构体,在其中指定处理函数oldact:用于保存原处理方式,可为NULLsignal()的增强版,功能更强大,可精确控制信号处理行为
fcntl()(F_SETOWN)int fcntl(int fd, int cmd, ... /* arg */);fd:文件描述符cmdF_SETOWNarg:进程ID(getpid()fd:已打开的设备文件描述符cmdF_SETOWN表示设置文件拥有者arg:指定接收信号的进程PID将当前进程设置为设备文件的“拥有者”,告诉内核/驱动信号该发给谁
fcntl()(F_SETFL | FASYNC)int fcntl(int fd, int cmd, ... /* arg */);fd:文件描述符cmdF_SETFLargoflags | FASYNCfd:已打开的设备文件描述符cmdF_SETFL表示设置文件状态标志arg:在原标志基础上添加FASYNC位,开启异步通知模式开启设备的异步通知功能。执行后触发驱动中的.fasync回调函数
fcntl()(F_GETFL)int fcntl(int fd, int cmd, ... /* arg */);fd:文件描述符cmdF_GETFLfd:已打开的设备文件描述符cmdF_GETFL表示获取当前文件状态标志获取设备文件当前的状态标志,通常与F_SETFL配合使用,先获取原标志再添加FASYNC
2.驱动程序(内核空间)函数与结构体
函数/结构体函数原型/定义参数参数作用函数作用
struct fasync_structstruct fasync_struct { spinlock_t fa_lock; int magic; int fa_fd; struct fasync_struct *fa_next; struct file *fa_file; struct rcu_head fa_rcu; };—(结构体成员)fa_lock:自旋锁,保护结构体fa_fd:文件描述符fa_next:指向下一个节点的指针(单链表)fa_file:指向struct file的指针,内含进程PID等信息异步通知队列的节点结构体,用于维护所有“订阅”了该设备异步通知的进程信息
.fasync(file_operations成员)int (*fasync) (int fd, struct file *filp, int on);fd:文件描述符filp:文件结构体指针on:FASYNC标志值fd:由fcntl传入的文件描述符filp:由fcntl传入的struct file指针on:FASYNC标志的开关状态(1开启/0关闭)当应用程序通过fcntl改变FASYNC标志时,内核自动回调此函数,驱动在此函数中调用fasync_helper()管理订阅队列
fasync_helper()int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);fd:文件描述符filp:文件结构体指针on:FASYNC标志值fappfasync_struct指针的指针fd:由.fasync传入filp:由.fasync传入on:由.fasync传入fapp:指向设备驱动中定义的fasync_struct指针内核辅助函数,用于向异步通知队列中添加或移除订阅者(进程)。开启时注册,关闭或释放时移除
kill_fasync()void kill_fasync(struct fasync_struct **fp, int sig, int band);fpfasync_struct指针的指针sig:要发送的信号编号band:事件类型标志fp:指向设备驱动中定义的fasync_struct指针sig:要发送的信号,通常为SIGIObandPOLL_IN(数据可读)或POLL_OUT(缓冲区可写)遍历异步通知队列,向所有已注册的进程发送指定信号,通知它们设备资源已就绪
.release中的清理release函数中调用fasync_helper(-1, filp, 0, &dev->async_queue)fasync_helper()相同,但fd-1on0fd=-1:表示要移除而非添加on=0:关闭FASYNC标志当设备文件被关闭时,自动将当前进程从异步通知队列中移除,防止向已退出的进程发送信号
kill_fasync()band参数取值
标志宏含义使用场景
POLL_IN设备数据可读告知应用程序有数据到达,可以执行read()
POLL_OUT设备缓冲区可写告知应用程序设备发送缓冲区有空闲,可以执行write()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 2:15:34

HFSS vs ADS 滤波器仿真对比:500MHz 带通 3 方案性能与精度分析

HFSS与ADS滤波器仿真深度对比:500MHz带通设计的3种技术路线实战解析 在射频前端设计中,带通滤波器的性能直接影响系统整体表现。当工程师面对500MHz中心频率的带通滤波器设计需求时,工具选择往往成为首要难题:是采用ADS进行快速电…

作者头像 李华
网站建设 2026/7/7 2:07:51

PyTorch 2.x 模型量化实战:3种PTQ方法精度对比与部署指南

PyTorch 2.x 模型量化实战:3种PTQ方法精度对比与部署决策树在边缘计算和移动端部署场景中,模型量化已成为降低计算资源消耗、提升推理速度的关键技术。PyTorch 2.x 对量化工具链进行了全面升级,本文将深入解析三种训练后量化(PTQ&…

作者头像 李华