Q:什么是信号驱动I/O?
A:对于给定的I/O口(一般就是对于文件描述符)设定为信号驱动I/O,则当I/O口准备好之后(读:有数据可读;写:有空间可写),向注册它的进程发送事先约定好的信号,进程收到信号后触发signal handler进行I/O处理。
Q:Linux下信号驱动I/O的注册方法?
A:系统设定两种信号为专用信号:SIGIO和SIGURG,这两种都是非实时、不可靠信号,不能参加排队。前者是通用信号,后者则专门用于网络传输中带外数据(紧急数据)的I/O,本文不涉及。具体步骤如下:
1. 对文件描述符调用fcntl函数,设置O_NONBLOCK和O_ASYNC(只能对终端和网络的文件描述符使用)属性;
fcntl(fd, F_SETFL, old | O_NONBLOCK | O_ASYNC)
2. 对文件描述符设置SIGIO、SIGURG信号所通知的进程,一般是调用进程本身;
fcntl(fd, F_SETOWN, getpid())
3. 编写signal handler函数,并使用signal或sigaction函数注册。
这就是APUE书上所说的BSD异步I/O方法。严格的说信号驱动I/O不是异步的I/O,因为它I/O的操作本身仍然是同步的(需要进程主动去读取或写入),只是使用了signal这种异步机制通知进程什么时候该读写了,属于伪异步。
实验一:将标准输入注册为信号驱动I/O
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
void sigio_handler(int sig)
{
static int cnt = 0;
printf("receive SIGIO signal %d\n", ++cnt);
fflush(stdin);
fflush(stdout);
}
int main(int argc, char **argv)
{
struct sigaction sig_act;
int fl = 0;
//signal driven I/O setting
fflush(stdin);
fflush(stdout);
fl = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK | O_ASYNC );
fcntl(STDIN_FILENO, F_SETOWN, getpid());
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = 0;
sig_act.sa_handler = sigio_handler;
sigaction(SIGIO, &sig_act, NULL);
printf("test start\n");
fflush(stdin);
fflush(stdout);
while (1)
{
sleep(1);
}
return 0;
}
实验结果:循环打印signal handler中的语句,如下:
test start
receive SIGIO signal 1
receive