I/O模型
阻塞与非阻塞I/O
阻塞和非阻塞主要是指调用某个系统函数时,这个函数是否会导致我们的进程进入sleep()【卡在这休眠】状态而言的;
阻塞I/O
我调用一个函数,这个函数就卡在在这里,整个程序流程不往下走了【休眠sleep】,该函数卡在这里等待一个事情发生,只有这个事情发生了,这个函数才会往下走;这种函数,就认为是阻塞函数;accept();【listenfd套接字阻塞,因此accept阻塞】
这种阻塞,并不好,效率很低;一般我们不会用阻塞方式来写服务器程序,效率低;
非阻塞I/O:
不会卡住,充分利用时间片,执行更高;
非阻塞模式的两个鲜明特点:
(1)不断的调用accept(),recvfrom()函数来检查有没有数据到来,如果没有,函数会返回一个特殊的错误标记来告诉你,这种标记可能是EWULDBLOCK,,也可能是EAGAIN;如果数据没到来,那么这里有机会执行其他函数,但是也得不停的再次调用accept(),recvfrom()来检查数据是否到来,非常累;因此我采用了epoll技术!!!!不用来回调用了。
(2)如果数据到来,那么就得卡在这里把数据从内核缓冲区复制到用户缓冲区,所以复制这个阶段是卡着完成的;
同步与异步I/O
异步I/O
调用一个异步I/O函数时,要给这个函数指定一个接收缓冲区,还要给定一个回调函数;
调用完一个异步I/O函数后,该函数会立即返回。 其余判断交给操作系统,操作系统会判断数据是否到来,如果数据到来了,操作系统会把数据拷贝到你所提供的缓冲区里,然后调用你所指定的这个回调函数来通知你;
很容易区别非阻塞和异步I/O的差别:
- 非阻塞I/O要不停的调用I/O函数来检查数据是否来,如果数据来了,就得卡在I/O函数这里把数据从内核缓冲区复制到用户缓冲区,然后这个函数才能返回;
- 异步I/O根本不需要不停的调用I/O函数来检查数据是否到来,只需要调用一次,然后就可以干别的事情去了;内核判断数据到来,拷贝数据到你提供的缓冲区,调用你的回调函数来通知你,你并没有被卡在那里的情况;
同步I/O
select/poll。epoll。
- 调用select()判断有没有数据,有数据,走下来,没数据卡在那里;
- select()返回之后,用recvfrom()去取数据;当然取数据的时候也会卡那么一下;
同步I/O感觉更麻烦,要调用两个函数才能把数据拿到手;
但是同步I/O和阻塞式I/O比,就是所谓的 I/O复用【用两个函数来收数据的优势】 能力;
I/O复用
所谓I/O复用,就是我多个socket【多个TCP连接】可以弄成一捆【一堆】,我可以用select这种同步I/O函数在这等数据;
select()的能力是等多条TCP连接上的任意一条有数据来;,然后哪条TCP有数据来,我再用具体的比如recvfrom()去收。
所以,这种调用一个函数能够判断一堆TCP连接是否来数据的这种能力,叫I/O复用,英文I/O multiplexing【I/O多路复用】
信号io驱动模型
信号驱动IO模型,应用进程告诉内核:当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,并且调用我的信号处理函数来获取数据报。
总结
很多资料把 阻塞I/O,非阻塞I/O,同步I/O归结为一类 ,因为他们多多少少的都有阻塞的行为发生;甚至有的资料直接就把 阻塞I/O,非阻塞I/O 都归结为同步I/O模型,这也是可以的,而把异步I/O单独归结为一类,因为异步I/O是真正的没有阻塞行为发生的;
思考题
什么叫 用 异步的方法 去使用 非阻塞调用 ?