Linux 异步通知机制(fasync / SIGIO)简析
适用内核版本:2.6.x ~ 6.x
目录
- 第 1 章 异步通知机制概述
- 第 2 章 应用层视角:如何接收异步通知
- 第 3 章 驱动层视角:如何发送异步通知
- 第 4 章 内核版本演进:从 Device Tree 到 6.x
- 第 5 章 机制深入解析
- 第 6 章 最佳实践与常见问题
第 1 章 异步通知机制概述
1.1 什么是异步通知
Linux 异步通知(Asynchronous Notification),又称 Signal-Driven I/O,是一种让内核在设备状态变化时主动通知用户进程的机制。其核心思想是:
将硬件中断的响应模型映射到用户空间进程层面——当外设产生事件(如按键按下、数据到达、DMA 完成)时,驱动程序不等待用户轮询,而是主动向注册了异步通知的进程发送 SIGIO 信号。
该机制规避了轮询的 CPU 浪费,也避免了阻塞 I/O 导致的进程挂起,复用了 Linux 成熟的信号基础设施,体现了内核设计的模块化与复用性原则。
1.2 异步通知 vs 异步 I/O vs 阻塞/非阻塞 I/O
| 触发主体 | 驱动在资源就绪时主动发起 | 应用主动发起 I/O 请求 | 应用主动发起读写 |
| 核心机制 | 信号 (SIGIO) | io_submit() / io_getevents() | read() / write() |
| 回调执行 | 用户空间信号处理函数 | 内核完成 I/O 后返回结果 | 无回调,直接返回 |
| 资源访问 | 仅通知"资源可用",后续仍需显式 read()/write() | 内核自动完成数据搬运 | 应用自己完成数据搬运 |
| 适用场景 | 状态变化检测(按键、传感器、串口触发) | 高吞吐量磁盘/网络 I/O | 简单同步数据流 |
⚠️ 重要区分:O_ASYNC(曾用名 FASYNC)启用的是信号驱动 I/O(通知"就绪"),而非 POSIX AIO(通知"完成")。两者名称相似但语义完全不同。
1.3 整体架构

上图展示了异步通知的四层架构:
第 2 章 应用层视角:如何接收异步通知
2.1 核心 API 介绍
2.1.1 signal() / sigaction() —— 注册信号处理函数
#include <signal.h>
/* 方式一:signal() —— 简单但可移植性较弱 */
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
/* 方式二:sigaction() —— 推荐,支持 SA_SIGINFO 获取 siginfo_t */
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数说明:
- signum:信号编号,异步通知固定使用 SIGIO(编号 29 on x86,23 on ARM)。
- handler:信号处理函数。使用 sigaction 且设置 SA_SIGINFO 时,可接收 siginfo_t * 参数,从中获取触发信号的 fd(si_fd)与事件类型(si_band)。
返回值:成功返回 0;失败返回 -1 并设置 errno。
2.1.2 fcntl() —— 设置文件属主与 FASYNC 标志
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
关键命令:
| F_SETOWN | 设置接收 SIGIO 的进程/进程组 PID | fcntl(fd, F_SETOWN, getpid()) |
| F_GETFL | 获取文件状态标志 | fcntl(fd, F_GETFL) |
| F_SETFL | 设置文件状态标志(或上 FASYNC/O_ASYNC) | fcntl(fd, F_SETFL, flags | FASYNC) |
| F_SETSIG | 指定替代 SIGIO 的实时信号(可选) | fcntl(fd, F_SETSIG, SIGRTMIN) |
注意:FASYNC 与 O_ASYNC 在 Linux 下语义等价,glibc 将 FASYNC 定义为 O_ASYNC 的别名。但 O_ASYNC 是标准名称,建议优先使用。
2.2 应用层启用异步通知的三步骤
/* 步骤 1:注册 SIGIO 信号处理函数 */
signal(SIGIO, sigio_handler); // 或使用 sigaction()
/* 步骤 2:设置当前进程为文件属主 */
fcntl(fd, F_SETOWN, getpid());
/* 步骤 3:启用 FASYNC 标志 */
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
完成以上三步后,一旦驱动调用 kill_fasync() 发送 SIGIO,当前进程就会进入 sigio_handler。
2.3 完整应用示例
/* app_async_notify.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
static volatile int async_flag = 0;
static int dev_fd = –1;
/* SIGIO 信号处理函数 */
static void sigio_handler(int sig, siginfo_t *info, void *context)
{
(void)sig;
(void)context;
/* 通过 siginfo_t 获取触发源 fd 与事件类型 */
if (info) {
printf("[APP] Received SIGIO, si_fd=%d, si_band=%ld\\n",
info->si_fd, info->si_band);
}
async_flag = 1;
}
int main(int argc, char *argv[])
{
char buf[256];
struct sigaction sa;
if (argc < 2) {
fprintf(stderr, "Usage: %s <device>\\n", argv[0]);
return EXIT_FAILURE;
}
/* 打开设备 */
dev_fd = open(argv[1], O_RDWR);
if (dev_fd < 0) {
perror("open");
return EXIT_FAILURE;
}
/* 步骤 1:使用 sigaction 注册,带 SA_SIGINFO 可获取详细信息 */
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigio_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGIO, &sa, NULL) < 0) {
perror("sigaction");
close(dev_fd);
return EXIT_FAILURE;
}
/* 步骤 2:设置属主 */
if (fcntl(dev_fd, F_SETOWN, getpid()) < 0) {
perror("fcntl F_SETOWN");
close(dev_fd);
return EXIT_FAILURE;
}
/* 步骤 3:启用 FASYNC */
int flags = fcntl(dev_fd, F_GETFL);
if (fcntl(dev_fd, F_SETFL, flags | FASYNC) < 0) {
perror("fcntl F_SETFL FASYNC");
close(dev_fd);
return EXIT_FAILURE;
}
printf("[APP] Async notification enabled, waiting for SIGIO…\\n");
/* 主循环:非阻塞执行其他任务,仅在收到信号时读取 */
while (1) {
/* 模拟其他业务逻辑 */
for (int i = 0; i < 10 && !async_flag; i++) {
usleep(100000); /* 100ms */
putchar('.'); fflush(stdout);
}
if (async_flag) {
async_flag = 0;
memset(buf, 0, sizeof(buf));
int n = read(dev_fd, buf, sizeof(buf) – 1);
if (n > 0) {
printf("\\n[APP] Read %d bytes: %s\\n", n, buf);
}
}
}
close(dev_fd);
return 0;
}
2.4 应用层使用注意事项
边缘触发(Edge-Triggered)风险:SIGIO 是边缘触发的。若收到信号后未将数据全部读走,剩余数据不会再次触发 SIGIO,程序将永远挂起。收到信号后必须循环读取直到返回 EAGAIN。
信号处理函数限制:信号处理函数处于异步上下文,只能调用**异步信号安全(async-signal-safe)**函数。禁止在信号处理函数中调用 malloc()、printf()(标准 I/O 非安全)、pthread_mutex_lock() 等。示例中的 printf 仅用于演示,生产环境应使用 write(STDOUT_FILENO, …) 或设置标志位由主循环处理。
多线程环境:F_SETOWN 设置的是进程级属主。若使用 F_SETOWN_EX(Linux 2.6.32+),可精确指定线程 ID(TID),避免信号被错误线程接收。
实时信号替代:通过 fcntl(fd, F_SETSIG, SIGRTMIN) 可改用实时信号(SIGRTMIN~SIGRTMAX),支持排队与携带 siginfo_t,避免标准信号丢失问题。
文件关闭前清理:应用退出前只需 close(fd),驱动的 release() 会自动清理异步队列。但显式先清除 FASYNC 标志是更安全的做法。
第 3 章 驱动层视角:如何发送异步通知
3.1 核心数据结构
3.1.1 struct fasync_struct
/* include/linux/fs.h */
struct fasync_struct {
int magic; /* 魔数校验:FASYNC_MAGIC */
int fa_fd; /* 关联的文件描述符 */
struct fasync_struct *fa_next; /* 异步队列链表指针 */
struct file *fa_file; /* 关联的 struct file 指针 */
};
每个打开该设备的进程都会在内核中对应一个 fasync_struct 节点,驱动通过链表管理所有等待异步通知的进程。
3.1.2 struct fown_struct
位于 struct file 中的 f_owner 字段,记录了接收信号的进程/进程组信息:
struct fown_struct {
rwlock_t lock; /* 5.x+ 引入读写锁细化并发 */
struct pid *pid; /* 目标进程 */
enum pid_type pid_type; /* PIDTYPE_PID / PIDTYPE_PGID */
uid_t uid, euid;
int signum; /* F_SETSIG 指定的信号,0 表示默认 SIGIO */
};
3.2 核心 API 介绍
3.2.1 fasync_helper() —— 异步队列管理
#include <linux/fs.h>
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);
功能:当用户空间通过 fcntl 改变 FASYNC 标志时,内核调用驱动的 .fasync 方法,驱动再将参数透传给 fasync_helper,由后者完成 fasync_struct 节点的分配/释放与链表维护。
参数:
- fd:文件描述符(来自 .fasync 方法参数);
- filp:struct file 指针(来自 .fasync 方法参数);
- on:非 0 表示添加节点,0 表示删除节点;
- fapp:指向驱动中 struct fasync_struct * 指针的地址。
返回值:
- > 0:成功添加或删除了节点;
- 0:未做任何修改;
- < 0:内存分配失败(-ENOMEM)。
3.2.2 kill_fasync() —— 发送异步信号
#include <linux/fs.h>
void kill_fasync(struct fasync_struct **fp, int sig, int band);
功能:遍历 fp 指向的异步队列,向每个注册的进程发送指定信号。
参数:
- fp:指向驱动中 struct fasync_struct * 指针的地址(即 &dev->async_queue);
- sig:信号编号,通常为 SIGIO;
- band:事件类型,常用 POLL_IN(可读)、POLL_OUT(可写)、POLL_ERR(错误)。
注意:kill_fasync() 内部会调用 send_sigio() → send_sig_info(),最终通过信号子系统将信号插入目标进程的挂起队列。该函数可在中断上下文安全调用。
3.3 驱动实现四步骤
/* 步骤 1:在设备结构体中定义 fasync_struct 指针 */
struct my_dev {
struct fasync_struct *async_queue;
/* … 其他字段 … */
};
/* 步骤 2:实现 .fasync 方法 */
static int my_fasync(int fd, struct file *filp, int on)
{
struct my_dev *dev = filp->private_data;
return fasync_helper(fd, filp, on, &dev->async_queue);
}
/* 步骤 3:在数据就绪/事件发生时调用 kill_fasync */
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
struct my_dev *dev = dev_id;
/* … 处理硬件事件 … */
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
/* 步骤 4:在 release() 中清理 */
static int my_release(struct inode *inode, struct file *filp)
{
struct my_dev *dev = filp->private_data;
/* 参数 fd=-1, on=0 表示强制移除 */
my_fasync(–1, filp, 0);
return 0;
}
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.fasync = my_fasync,
.release = my_release,
/* … */
};
3.4 完整驱动示例
/* drv_async_notify.c —— 简化版字符设备驱动 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/poll.h>
#define DEVICE_NAME "async_demo"
#define CLASS_NAME "async_cls"
static int major;
static struct class *async_class;
static struct device *async_device;
static struct cdev async_cdev;
static DECLARE_WAIT_QUEUE_HEAD(read_waitq);
static volatile int data_ready = 0;
static char kbuf[256];
static struct fasync_struct *async_queue = NULL;
/* === .fasync 方法 === */
static int async_demo_fasync(int fd, struct file *filp, int on)
{
printk(KERN_INFO "[%s] fasync called: fd=%d, on=%d\\n", DEVICE_NAME, fd, on);
return fasync_helper(fd, filp, on, &async_queue);
}
/* === 模拟中断/事件触发 === */
static ssize_t async_demo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
size_t n = min(count, sizeof(kbuf) – 1);
if (copy_from_user(kbuf, buf, n))
return –EFAULT;
kbuf[n] = '\\0';
data_ready = 1;
/* 唤醒阻塞读 */
wake_up_interruptible(&read_waitq);
/* 发送异步通知 */
if (async_queue) {
kill_fasync(&async_queue, SIGIO, POLL_IN);
printk(KERN_INFO "[%s] SIGIO sent to registered processes\\n", DEVICE_NAME);
}
return n;
}
/* === read 方法 === */
static ssize_t async_demo_read(struct file *filp, char __user *buf,
size_t count, loff_t *pos)
{
wait_event_interruptible(read_waitq, data_ready);
size_t n = min(count, strlen(kbuf));
if (copy_to_user(buf, kbuf, n))
return –EFAULT;
data_ready = 0;
return n;
}
/* === release 方法:必须清理异步队列 === */
static int async_demo_release(struct inode *inode, struct file *filp)
{
async_demo_fasync(–1, filp, 0);
printk(KERN_INFO "[%s] release: async queue cleaned\\n", DEVICE_NAME);
return 0;
}
static struct file_operations async_demo_fops = {
.owner = THIS_MODULE,
.write = async_demo_write,
.read = async_demo_read,
.fasync = async_demo_fasync,
.release = async_demo_release,
};
static int __init async_demo_init(void)
{
dev_t devno;
alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
major = MAJOR(devno);
cdev_init(&async_cdev, &async_demo_fops);
cdev_add(&async_cdev, devno, 1);
async_class = class_create(THIS_MODULE, CLASS_NAME);
async_device = device_create(async_class, NULL, devno, NULL, DEVICE_NAME);
printk(KERN_INFO "[%s] loaded, major=%d\\n", DEVICE_NAME, major);
return 0;
}
static void __exit async_demo_exit(void)
{
device_destroy(async_class, MKDEV(major, 0));
class_destroy(async_class);
cdev_del(&async_cdev);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk(KERN_INFO "[%s] unloaded\\n", DEVICE_NAME);
}
module_init(async_demo_init);
module_exit(async_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Linux Async Notification Demo Driver");
3.5 驱动层使用注意事项
必须实现 .fasync 方法:若 file_operations 中未提供 .fasync,用户空间设置 FASYNC 将不会生效,内核无法建立异步队列。
kill_fasync() 前判空:虽然 kill_fasync() 内部有保护,但显式判断 if (dev->async_queue) 可避免不必要的函数调用开销,且代码语义更清晰。
release() 中强制清理:调用 .fasync(-1, filp, 0) 将当前 filp 从异步队列中移除。若遗漏此步骤,进程关闭 fd 后仍可能收到信号,导致 SIGIO 被投递到已不存在的进程上下文。
中断上下文安全:kill_fasync() 内部使用 spin_lock_irqsave(&fasync_lock, …) 保护链表遍历,因此可在硬中断/软中断/进程上下文任意调用。但建议在中断上半部(ISR)中快速调用后立即返回,复杂逻辑移至下半部(tasklet/workqueue)。
多进程并发访问:fasync_helper() 内部使用全局 fasync_lock(自旋锁)保护链表操作。驱动无需额外加锁,但需注意 fasync_struct 指针本身的内存可见性(建议用 struct fasync_struct * 而非裸指针数组)。
第 4 章 内核版本演进:从 Device Tree 到 6.x

4.1 2.6.x 时期:基础框架成熟与 Device Tree 萌芽
- 2.3.21+:kill_fasync() 引入第三个参数 band(POLL_IN/POLL_OUT),在此之前仅有两个参数(struct fasync_struct *queue, int signal)。
- 2.6.x:fasync_helper() 与 kill_fasync() 接口完全定型,成为字符设备驱动标准范式。
- Device Tree 萌芽:2005~2008 年,PowerPC 架构率先引入 Device Tree 以解决嵌入式板级代码臃肿问题。此时 ARM 仍使用硬编码的 arch/arm/mach-*/board-*.c。
4.2 3.x 时期:Device Tree 标准化与 ARM 架构统一
- Linux 3.0 (2011):Device Tree 正式合并入主线,成为 ARM SoC 的标准硬件描述方式。
- 对异步通知的影响:platform_driver 开始通过 of_match_table 绑定 Device Tree 节点。驱动开发者不再需要关心板级差异,.fasync 的实现逻辑与硬件描述解耦,驱动可复用性大幅提升。
- RCU 保护引入:3.x 后期开始对 fasync 相关链表进行 RCU 读端保护,减少 fasync_lock 自旋锁的争用。
4.3 4.x 时期:安全增强与实时信号完善
- 4.14+:send_sigio() 内部实现增强,对 siginfo_t 的构造更加严谨,si_uid 与 si_fd 的填充逻辑修正了早期版本中的重叠问题。
- 实时信号支持:fcntl(fd, F_SETSIG, rt_signal) 在 4.x 系列中得到完善,实时信号可携带完整的 siginfo_t 信息,解决了标准 SIGIO 不排队、可能丢失的问题。
- cgroup v1 信号隔离:4.x 引入的 cgroup 机制开始对信号投递进行命名空间级隔离,确保容器内进程不会收到容器外设备的 SIGIO。
4.4 5.x 时期:并发优化与周边子系统协同
- 5.x:struct file 中的 f_owner 字段从简单的读写锁保护升级为更细粒度的 rwlock_t,减少了高并发场景下多进程同时注册/注销 FASYNC 的锁争用。
- lockdep 增强:内核死锁检测工具 lockdep 增加了对 fasync_lock 的依赖图分析,帮助驱动开发者发现潜在的死锁风险。
- 无核心 API 变更:fasync_helper() 与 kill_fasync() 的函数原型在 5.x 全系列保持不变,体现了内核 API 的向后兼容承诺。
4.5 6.x 时期:向后兼容与新范式并存
- 6.x:fasync 机制本身无破坏性变更,保持完全向后兼容。但内核社区更推荐使用 io_uring + poll 作为高性能异步事件通知的新范式。
- cgroup v2 信号隔离:6.x 进一步完善了 cgroup v2 下的信号投递策略,支持通过 cgroup.subtree_control 对 SIGIO 进行层级隔离。
- BPF 与 fasync:6.x 引入了更多 BPF 钩子点,允许在信号投递路径上挂载 BPF 程序进行审计或过滤,为安全敏感场景提供了新选择。
4.6 版本差异速查表
| 2.0 | inode 参数 | 两参数(无 band) | int (*fasync)(struct inode*, struct file*, int) | 早期实现 |
| 2.3.21+ | 稳定接口 | 三参数(+band) | int (*fasync)(int fd, struct file*, int) | band 参数引入 |
| 2.6.x | 标准接口 | POLL_IN/OUT | 同上 | 设备树萌芽(PowerPC) |
| 3.x+ | RCU 保护 | send_sigio 增强 | 同上 | ARM DT 标准化 |
| 4.14+ | fasync_lock | siginfo_t 完善 | 同上 | 安全与实时性增强 |
| 5.x+ | 无变化 | 无变化 | 同上 | 周边子系统优化 |
| 6.x+ | 向后兼容 | 向后兼容 | 同上 | io_uring 替代趋势 |
第 5 章 机制深入解析
5.1 SIGIO 信号投递完整流程

上图将信号投递划分为四个阶段:
阶段 1 —— 硬件触发:外设产生事件 → 中断控制器(GIC/APIC)分发 → CPU 响应 IRQ → 进入 ISR(关中断/保存上下文)。
阶段 2 —— 驱动响应:ISR 读取状态寄存器确认事件源 → 调用 kill_fasync() → 遍历 fasync_struct 异步队列链表 → 获取 filp->f_owner 中的目标进程 PID。
阶段 3 —— 内核投递:send_sigio() 构造 siginfo_t(填充 si_signo=SIGIO、si_code=SI_SIGIO、si_fd=fa_fd、si_band=POLL_IN)→ send_sig_info() 将信号插入目标进程的 shared_pending 队列 → 进程调度时检查并递送信号。
阶段 4 —— 用户处理:进程从内核态返回用户态 → do_signal() 处理挂起信号 → 调用用户注册的 SIGIO 信号处理函数 → 用户程序执行 read() 或业务逻辑。
5.2 fasync_helper() 内部实现要点
/* 简化逻辑,基于 4.14+ 内核 */
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
struct fasync_struct *new = NULL;
int result = 0;
if (on) {
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new)
return –ENOMEM;
}
write_lock_irq(&fasync_lock); /* 全局自旋锁保护 */
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp) { /* 已存在该 filp 的节点 */
if (on) {
fa->fa_fd = fd; /* 更新 fd */
kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next; /* 从链表摘除 */
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
}
}
if (on) {
new->magic = FASYNC_MAGIC; /* 魔数校验,防止内存损坏 */
new->fa_file = filp;
new->fa_fd = fd;
new->fa_next = *fapp;
*fapp = new;
result = 1;
}
out:
write_unlock_irq(&fasync_lock);
return result;
}
关键设计:
- 使用 slab 缓存(fasync_cache)分配节点,避免频繁调用通用内存分配器;
- 使用 全局自旋锁 fasync_lock(write_lock_irq)保证链表操作的原子性;
- 通过 fa_file == filp 匹配节点,确保每个 struct file 实例在链表中唯一。
5.3 边缘触发特性与数据排空
SIGIO 属于**边缘触发(Edge-Triggered)**通知:
仅当设备状态从"未就绪"跳变到"就绪"时发送一次信号。若应用未将数据全部读走,状态不会回退到"未就绪",因此不会产生第二次跳变,也就不会有新的 SIGIO。
正确做法:
static void sigio_handler(int sig)
{
int flags = fcntl(dev_fd, F_GETFL);
fcntl(dev_fd, F_SETFL, flags | O_NONBLOCK); /* 设为非阻塞 */
while (1) {
char buf[256];
int n = read(dev_fd, buf, sizeof(buf));
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break; /* 数据已排空 */
/* 其他错误处理 */
break;
}
/* 处理 buf 中的 n 字节数据 */
}
}
第 6 章 最佳实践与常见问题
6.1 最佳实践清单
| 信号注册 | 优先使用 sigaction() + SA_SIGINFO,避免 signal() 的可移植性问题 |
| 多线程 | 使用 F_SETOWN_EX 精确指定 TID,或统一由主线程接收后分发 |
| 信号安全 | 信号处理函数中仅修改 volatile sig_atomic_t 标志,主循环执行 I/O |
| 数据排空 | 收到 SIGIO 后循环 read() 直到 EAGAIN,防止边缘触发漏事件 |
| 驱动实现 | .fasync 方法必须透传参数给 fasync_helper();release() 中必须调用 .fasync(-1, filp, 0) |
| 实时性要求 | 使用 fcntl(F_SETSIG, SIGRTMIN) 替代 SIGIO,支持信号排队 |
| 高并发 | 考虑 epoll + O_NONBLOCK 或 io_uring 替代 fasync,减少信号开销 |
6.2 常见问题 FAQ
Q1:应用设置了 FASYNC,但收不到 SIGIO?
排查步骤:
Q2:kill_fasync() 可以在中断上下文调用吗?
可以。kill_fasync() → send_sigio() → send_sig_info() 全程使用自旋锁与 RCU,不调用可能睡眠的函数,因此可在硬中断、软中断、tasklet 中安全调用。
Q3:多个进程同时打开设备,都能收到 SIGIO 吗?
可以。fasync_helper() 为每个 filp 创建一个 fasync_struct 节点,所有节点挂在同一条链表上。kill_fasync() 会遍历链表向每个节点对应的进程发送信号。
Q4:6.x 内核下 fasync 是否已被废弃?
未被废弃,仍完全支持。但社区推荐新项目优先考虑 io_uring(通过 IORING_OP_POLL_ADD 实现异步事件通知),其性能与可扩展性优于信号驱动模型。
Q5:Device Tree 与异步通知有什么关系?
Device Tree 本身不直接改变 fasync 机制,但它改变了驱动的组织方式:
- 驱动通过 compatible 字符串与 DT 节点绑定,不再依赖板级文件;
- 中断资源通过 interrupts / interrupt-parent 属性描述,由内核自动解析并调用 request_irq();
- 驱动代码更纯粹,.fasync 的实现与硬件描述完全解耦。
网硕互联帮助中心





评论前必须登录!
注册