在UNIX/Linux下主要有四种I/O模型:
1、阻塞I/O:
最常用、最简单、效率最低
read();
//没有数据可读会阻塞,直到读到就返回
2、非阻塞I/O
可防止进程阻塞在I/O操作上,需要轮询
while(1)
{
ret = read();
if(ret > 0)
{
break;
}
}
3、I/O多路复用:
允许同时对多个I/O进行控制
4、信号驱动I/O
一种异步通信
void sigFunc(int sigNo)
{
read();
}
signal(SIGIO,sigFunc);
什么是复用?
一个东西,多种功能,不同时候,使用其中某一个功能
IO多路复用:一个进程可以处理多路IO,多路IO互不干扰
一个进程某一小段时间只能处理一路IO,如何实现一个进程处理多路IO的效果呢?
哪路IO准备好了就处理哪路IO(有数据到来可以读取或者写缓冲区有空闲可以写入),处理一路IO完了之后如果有其他路IO也准备好了,那再处理其他路IO,给我们感觉是多路IO在同时被处理,原因是因为每一路IO在被处理的时候时间是比较短,也就是说不适合处理一次时间比较长的IO
I/O多路复用其基本思想是:
先构造一张有关文件描述符(一切皆文件,IO操作就是文件操作)的表,然后调用一个函数(检查有无IO准备好,如果准备好对应的位保持不变,否则清0)。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
先构造一张有关文件描述符(一切皆文件,IO操作就是文件操作)的表:是一个一共有1024位(128字节,假设是32位的系统,元素类型是long,也就是32个元素)的数组,1024位每一个位都代表一个文件描述符,也就是这个表一共可以表示1024个文件描述符(1024路IO),第0位就是文件描述符0,第1位就是文件描述符1
为什么用1个位表示一个文件描述符呢?
1个位只有两种状态1和0,1个文件描述符就是一路IO,也就是IO有两种状态:准备好和没有准备好,准备好就处理,没有准备好就等着
int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);
功能:t函数判断有无文件描述符准备就绪,如果没有,就阻塞,如果有就返回,并将没有准备就绪的位置0
参数:
n 要遍历的次数,传的是最大的文件描述符+1
read_fds 要判断的需要读操作的文件描述符的表
write_fds 要判断的需要写操作的文件描述符的表
except_fds 要判断的有无异常的文件描述符的表
timeout NULL 表示阻塞
详细实现过程:
1、创建文件描述符的表 fd_set fds;
fd_set是一个结构体类型的别名,这个结构体里只有一成员,就是一个有32个long类型元素的数组,所以fd_set就占1024位
2、将表清空 FD_ZERO(&fds);
3、将要被这个进程处理的IO的文件描述符加入表中(对应的位置1)
4、将这个表交给select函数判断有无文件描述符准备就绪,如果没有,就阻塞,如果有就返回,并将没有准备就绪的位置0