1.select和poll
IO多路转换技术, select, poll的原理是: 通过将待监听文件描述符(fd)加入集合, 然后通过查询其调用返回值, 取得数据有动静的fd数量, 再轮询集合中每个fd, 如果有数据, 就直接读取; 如果没有数据, 就等待下一次查询.
select和poll实现了异步形式通知, 但本质上还是需要主动轮询.
2. BSD异步IO
System V和BSD都有一套各自的异步IO, 原理类似, 这里只介绍BSD异步IO.
BSD异步IO是信号SIGIO, SIGURG的组合: SIGIO 通用异步IO信号; SIGURG 用来通知进程网络连接上的带外数据(data of band, 紧急数据)已经达到.
进程接收SIGIO信号, 需要执行的步骤:
- 调用signal/sigaction为SIGIO信号建立处理程序;
- 调用fcntl, 命令参数F_SETOWN, 设置进程ID或进程组ID, 用于告诉驱动程序/内核, 指定进程接收SIGIO信号;
- 调用fcntl, 命令参数F_SETFL, 设置O_ASYNC文件状态标识, 以便在该fd上可以进行异步IO;
进程接收SIGURG, 只需只需第1,2步. 信号仅对引用支持带外数据的网络连接描述符 产生, 如TCP连接(UDP不支持).
BSD异步IO例程
完整源代码, 请参见 async.c
关键步骤代码
void sig_fun() {
int data = 0;
int n = read(mousefd, &data, sizeof(data));
if (n < 0) {
printf("read mouse error\n");
}
else {
printf("%d\n", data);
}
}
struct sigaction sa;
struct sigaction od_sa;
sa.sa_handler = sig_fun;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGIO); // SIGIO添加进信号集
sa.sa_flags = 0;
// 1. 调用sigaction为SIGIO信号建立信号处理程序
sigaction(SIGIO, &sa, &od_sa); // 捕获SIGIO后, 处理信号时, 阻塞信号; 处理完毕后恢复
// 2. 以命令F_SETOWN调用fcntl来设置进程ID, 用于接收对该描述符的信号(SIGIO)
if (fcntl(mousefd, F_SETOWN, getpid()) < 0) {
perror("fcntl F_SETOWN error");
exit(1);
}
// 3. 以命令F_SETFL调用fcntl, 设置O_ASYNC文件状态标识, 使得在该描述符上可以进行异步IO
int flag = fcntl(mousefd, F_GETFL);
if (flag < 0) {
perror("fcntl F_GETFL error");
exit(1);
}
flag |= O_ASYNC;
ret = fcntl(mousefd, F_SETFL, flag);
if (ret < 0) {
perror("fcntl F_SETFL error");
exit(1);
}
while (1) {
usleep(50);
}
3. POSIX异步IO(AIO)
BSD对不同的设备文件进行异步IO方法不一样, 如终端设备是产生SIGIO信号, 仅支持带外数据的设备才能产生SIGURG信号.
POSIX对不同类型文件进