一、什么是信号驱动 IO
信号驱动 I/O(Signal-Driven I/O) 是一种比较少见但很经典的 I/O 模型。它利用 Unix 信号机制,当 I/O 事件发生时,内核主动向进程发送信号通知。核心思想:当 socket 有数据时,内核向进程发送一个信号,进程在信号处理函数中进行读取。
常用信号:
SIGIO
流程:
进程注册 SIGIO 信号
↓
设置 socket 为异步模式
↓
当 socket 可读
↓
内核发送 SIGIO
↓
信号处理函数执行 recv()
特点:
- 不需要轮询
- 不需要阻塞等待
- 内核主动通知
二、信号驱动 IO 工作流程
完整流程如下:
应用程序
│
│ 注册 SIGIO 信号处理函数
│
│ 设置 socket 为 O_ASYNC
│
│ 指定信号接收进程
▼
内核
│
│ 监听 socket
│
│ 数据到达
│
▼
发送 SIGIO 信号
│
▼
用户进程收到信号
│
▼
信号处理函数执行
│
▼
recv() 读取数据

三、需要的三个关键步骤
① 注册信号处理函数
signal(SIGIO, sigio_handler);
或者更安全:
sigaction(SIGIO, &act, NULL);
② 设置 socket 所属进程
fcntl(fd, F_SETOWN, getpid());
告诉内核:SIGIO 信号发给哪个进程
③ 开启异步通知
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_ASYNC);
O_ASYNC 表示:
当 socket 有 IO 事件时
发送 SIGIO 信号
四、完整示例代码
1️⃣ 信号处理函数
void sigio_handler(int signo)
{
char buf[1024];
int n = recv(sockfd, buf, sizeof(buf), 0);
if (n > 0)
{
printf("receive: %s\n", buf);
}
}
2️⃣ 初始化 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
signal(SIGIO, sigio_handler);
fcntl(sockfd, F_SETOWN, getpid());
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_ASYNC);
3️⃣ 主线程
while (1)
{
printf("doing other work...\n");
sleep(1);
}
五、信号驱动 IO 的优点与缺点
优点:
- 不需要轮询:CPU 不需要遍历 fd。
- 不阻塞线程:程序可以继续执行其它逻辑。
- 响应速度快:信号立即触发。
缺点:
- 信号处理复杂:不能执行复杂逻辑、不能调用很多系统函数
- 信号可能丢失:如果信号触发过快;如(SIGIO SIGIO SIGIO);可能只处理一次。
- 可扩展性差:高并发服务器、多线程程序
- 调试困难:信号是异步触发的。

4456

被折叠的 条评论
为什么被折叠?



