高级IO:5种典型的IO模型/多路转接模型---三种实现
四种IO模型:阻塞IO/非阻塞IO/信号驱动IO/异步IO
IO的过程:发起IO调用,等待IO条件就绪,然后将数据拷贝到缓冲区进行处理---等待/拷贝
阻塞IO:为了完成IO,发起调用,若当前不具备IO条件,则一直等待
类似于钓鱼,没有鱼上钩就一直等待.
一个IO完毕后才能进行下一个,对于资源没有充分利用,大部分时间都在等待.
非阻塞IO:完了完成IO,发起调用,若当前不具备IO条件,则立即返回(通常干其他事情需要循环操作重新发起IO)
类似于钓鱼,鱼竿抛下去,有鱼则上钩,没鱼也不想一直等待,去干点其他事情.
流程相对于阻塞操作来说复杂一点,对资源利用更加充分,IO操作不够实时.
信号驱动IO:定义IO信号处理方法,在处理方式中进行IO操作,IO就绪时信号通知进程,进程在IO就绪时进行IO.(IO更加实时,对资源利用更加充分,流程更加复杂)
异步IO:通过异步IO调用告诉操作系统,IO哪些数据拷贝到哪里,IO的等待与拷贝都由操作系统完成.(资源利用更加充分,流程更加复杂)
阻塞:为了完成一个功能,发起调用,若当前不具备完成条件,则一直等待
非阻塞:为了完成一个功能,发起调用,若当前不具备完成条件,则立即报错返回
阻塞与非阻塞的区别:发起的调用在不具备完成条件的情况下是否立即返回
同步:处理流程中,顺序处理,一个完成之后再完成下一个,并且所有功能都由进程自身完成
异步:处理流程中,顺序不定,因为功能都由操作系统完成
同步与异步的区别:功能是否由进程自己完成,完成的顺序是否是确定.
异步阻塞:功能由别人完成,调用中等着别人完成.
异步非阻塞:功能由别人完成,调用是立即返回的.
多路转接IO:对大量的描述符进行IO事件监控.可以告诉进程现在有哪些描述符就绪了哪些事件,然后进程可直接只针对就绪了对应事件的描述符进行响应操作即可.避免对没有就绪的描述符进行IO操作所导致的效率降低/程序流程阻塞
只要需要对描述符进行监控的场景都可以使用多路转接模型
IO就绪事件:可读事件/可写事件/异常事件
例如:基本的TCP服务器程序,一个执行流中,既有accept,也有recv/send;然而每种操作都有可能在不满足条件的时候阻塞,若在大量的描述符中对一个没有就绪的描述符进行操作(对没有新连接的监听套接字调用accept/对没有数据到来的新的通信套接字recv都会导致流程阻塞,其他描述符就算就绪,也无法操作)
多路转接IO的实现:select/poll/epoll
监控的好处:让进程可以只针对就绪了指定事件的描述符进行操作,提高效率性能,避免了因为对没有就绪的描述符操作导致的阻塞。
select模型:
操作流程:
1.程序员定义某个事件的描述符集合(可读事件的描述符集合/可写事件的描述符集合/异常事件的描述符集合),初始化清空集合,对哪个描述符关心什么事件,就把这个描述符添加到相应事件的描述符集合中
2.发起监控调用,将集合拷贝到内核中进行监控,监控的原理是轮询遍历判断
可读事件的就绪:接收缓冲区数据大小大于低水位标记(量化标准--通常默认1个字节)
可写事件的就绪:发送缓冲区中剩余空间的大小大于低水位标记(量化标准--通常默认1个字节)
异常事件的就绪:描述符是否产生了某个异常
3.监控的调用返回,表示监控出错/有描述符就绪/监控等待超时
并且调用返回时,将事件监控的描述符集合中的未就绪描述符从集合中移除--(集合中仅仅保留就绪的描述符)
4.程序员轮询判断哪个描述符仍然还在哪个集合中,就确定这个描述符是否就绪了某个事件,然后进行对应事件的操作即可
select并不会直接返回给用户就绪的描述符直接操作,而是返回了就绪的描述符集合,因此需要程序员进行判断
代码操作:
1.定义集合--struct fd_set--成员只有一个数组--当作二进制位图使用--添加描述符就是将描述符的值对应的比特位置1
因此select能够监控的描述符数量,取决于二进制位图的比特位多少--而比特位多少取决于宏-FD_SETSIZE,默认等于1024
2.int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);