多路复用IO
-阻塞IO与非阻塞IO
-IO模型
IO的本质时基于操作系统接口来控制底层的硬件之间数据传输,并且在操作系统中实现了多种不同的IO方式(模型)比较常见的有下列三种:
1.阻塞型IO模型
2.非阻塞型IO模型
3.多路复用IO模型
-阻塞型IO
概念:当进程发出IO请求后,阻塞进程(让进程进入睡眠状态),资源就绪后唤醒进程继续执行(一般默认的IO操作都是阻塞型IO)
特点:会一直等待,直到数据就绪
-非阻塞型IO
特点:程序不会等待用户输入,会立即返回
当进程发出IO请求后,无论资源是否就绪都立即返回,相应的模型如下;
实现非阻塞型IO,需要设置O_NONBLOCK标志
fcntl函数:通过命令字(cmd)来设置文件描述符
-多路复用IO
-多路复用IO简介
本质:就是通过复用一个进程来处理多个IO请求
基本思想:由内核来监控多个文件描述符是否可以进行I/O操作,如果就绪的文件描述符,将结果告知用户进程,则用户在进程相应的I/O操作
-多路复用I/O方案
目前在linux系统有三种多路复用I/O的方案
1.select方案
2.poll方案
3.epoll方案
-select多路复用IO
-设计思想
1.通过单进程创建一个文件描述符集合,将需要监控的文件描述符,添加到这个集合中
2. 由内核负责监控文件描述符是否可以进行读写,一旦可以读写,则通知相应的进程进行相应的IO操作
select函数:监控一组文件描述符,阻塞当前进程,由内核监测相应的文件描述符是否就绪,一旦文件描述符就绪,将就绪的文件描述符拷贝给进程,唤醒进程处理
超时时间的说明:
1.如果timeout之后,文件描述符集合中没有任何就绪的文件描述符,select函数就会返回0
2.超时之后,timeout会被select函数修改,表示超时时间已经使用完
如果想继续使用使用超时间,需要备份之前的struct timval
超时之后,,表示没有就绪的文件描述符,此外文件描述符集合被赋值为空
因此,需要将之前的文件描述符集合进行备份
-select底层原理分析
-文件描述符集合
文件描述符集合的数组最终在储存时,使用了位图的方式来记录相应的文件描述符,具体原理如下:
1.数组中没有直接存储文件描述符,而是使用某一位来表示该文件描述符是否需要监控
2.需要监控的文件描述符需要转成数组的某一个元素的某一位,然后将对应的位设置为1
例如:当 fd = 60 的成员需要监控,则需要将数组的第0个成员的第 [60] bit 设置为1,当 fd = 64时,则需要将数组的第1个成员的第[0] bit 设置为1
(从上面的文件描述符集合内管理可以分析,select最终只能存储1024个文件描述符)
select基本原理如下:
在 select() 函数中一共需要使用三个文件描述符集合,分别是:
1.in: 需要进行读的文件描述符的集合
2.out:需要进行写的文件描述符的集合
3.exp:其他文件描述符集合
调用了select() 函数,内核做了如下事情:
1.从用户空间将集合的文件描述符拷贝到内核空间
2.循环遍历fd_set中所有的文件描述符,来检测是否有文件描述符可进行I/O操作
1>如果有文件描述符可进行I/O操作,则设置返回的文件描述符集合对应位为1(res_in,res_out,res_exp),跳出循环,直接返回。最终会赋值给in,out,exp 文件描述符集合
2>如果没有文件描述符可进行IO操作,则继续循环检测,如果设置timeout,则在超时后返回,此时select()函数返回0
select()函数减少了多进程/多线程的开销,但仍然有多缺点:
1.每次调用select()函数都需要将fd集合拷问到内核空间,这个开销在fd很多时就越大
2.每次都需要遍历所有的文件描述符集合,这个开销在fd很多时就越大
3.支持的文件描述符只有2024
整体流程
-poll多路复用IO
基本原理
1.在应用层是以struct polled数组的形式来进行管理文件描述符,在内核中基于链表对数组进行扩展
特点:
1.poll将请求与就绪事件通过结构体进行分开
2.select将请求与就绪文件描述符存储在同一个集合,导致每次都需要进行重新赋值才能进行下一次监控
3.在内核中仍然使用的是轮询的方式,与select相同,当文件描述符越来越多时,则会影响效率
POLLIN:普通数据可读
POLLOUT:普通数据可写
总体流程