阻塞与非阻塞I/O 阻塞和非阻塞主要是指调用某个系统函数时,这个函数是否会导致我们的进程进入 sleep()【卡在这休眠】状态而言的; a)阻塞I/O 我调用一个函数,这个函数就卡在在这里,整个程序流程不往下走了【休眠sleep】,该函数卡在这里等待一个事情发生, 只有这个事情发生了,这个函数才会往下走;这种函数,就认为是阻塞函数;accept(); //阻塞还是非阻塞的判断是根据监听套接字的种类 这种阻塞,并不好,效率很低;一般我们不会用阻塞方式来写服务器程序,效率低; b)非阻塞I/O:不会卡住,充分利用时间片,执行更高; 非阻塞模式的两个鲜明特点: (1)不断的调用accept(),recvfrom()函数来检查有没有数据到来,如果没有,函数会返回一个特殊的错误标记来告诉你,这种标记可能是EWULDBLOCK,也可能是EAGAIN; (不叫错误,叫标记)如果数据没到来,那么这里有机会执行其他函数,但是也得不停的再次调用accept(),recvfrom()来检查数据是否到来,非常累; //for(),不停的轮accept(),recvfrom(),依旧还是在一个进程中 (2)如果数据到来,那么操作就得卡在这里把数据从内核缓冲区复制到用户缓冲区,所以复制这个阶段是"卡着"完成的;
a)异步I/O: 事先准备工作:调用一个异步I/O函数时,我门要给这个函数指定一个“接收缓冲区”,我还要给定一个“回调函数”; 调用完一个异步I/O函数后,该函数会立即返回。 其余判断交给操作系统,操作系统会判断数据是否到来, 如果数据到来了,操作系统会把数据拷贝到你所提供的缓冲区里,然后调用你所指定的这个回调函数来通知你; //==>通知 很容易区别非阻塞和异步I/O的差别: (1)非阻塞I/O要不停的调用I/O函数来检查数据是否来, 如果数据来了,就得卡在I/O函数这里把数据从内核缓冲区复制到用户缓冲区,然后这个函数才能返回; (2)异步I/O根本不需要不停的调用I/O函数来检查数据是否到来,只需要调用一次,然后就可以干别的事情去了; 内核判断数据到来,拷贝数据到你提供的缓冲区,调用你的回调函数来通知你,你并没有被卡在那里的情况; b)同步I/O select/poll(能力不太行)。 epoll(最强)。 1)调用select()判断有没有数据,有数据,走下来,没数据卡在那里; 2)select()返回之后,用 recvfrom() 去取数据;当然取数据的时候也会卡那么一下; 同步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多路复用】==多用
分类
行业分类: 很多资料把 阻塞I/O,非阻塞I/O,同步I/O归结为一类 ,因为他们多多少少的都有阻塞的行为发生; 甚至有的资料直接就把 阻塞I/O,非阻塞I/O 都归结为同步I/O模型,这也是可以的】 而把异步I/O单独归结为一类,因为异步I/O是真正的没有阻塞行为发生的;