1、阻塞IO
我们通常使用read或者write来请求IO读写,通常IO操作都是阻塞IO,当我们调用read时,如果没有数据准备好就会导致线程或进程被挂起,直到有数据到来,能满足IO请求才会被唤醒。
所以在进程或者线程等待数据的过程是阻塞的。
2、非阻塞IO
但我们调用read时,如果没有数据带来就直接返回错误信息,而不是一直等待,这样就不会阻塞线程或者进程了,但是需要你不断的去轮询是否有数据到来或者是否能写。
3、IO多路复用
上面两种方式都是不完美的,所以出现了IO多路复用的概念,多路复用就是使用一个线程来检查所有的文件描述符的就绪状态,如果有一个文件描述符准备就绪就返回,得知这些sockt中有准备就绪的后对该文件描述符的操作就可以在一个线程中执行,也可以启动新的线程来执行。
image.png在同一个线程中,哪一个socket准备好就把开关放在哪个socket上实现数据的读写。所有的连接好像共享一条路。
4、IO多路复用实现
select:
该函数允许进程指示内核等待多个时间中的任意一个事件发生,并只在有一个或多个事件发生才唤醒进程。
调用select函数会传入一个数组,数组存放所有监听的文件描述符,需要从用户态拷贝到内核态,如果这个文件描述符很多的话,系统开销会增大。
select会返回已经准备就绪的socket个数,但是不会告诉你是哪一个socket准备就绪,这个时候就需要遍历所有的socket来找到就绪的socket,如果是上万个socket都找一遍需要消耗很多时间。
select监听的socket数量最大是1024。
poll:
poll和select很像,改进就是没有了1024个连接的限制。
epoll:
epoll修复了select和poll的很多问题,比如:
- epoll是线程安全的。
- epoll不经告诉我们socket数组中有已经准备就绪的描述符,好告诉是哪一个socket有数据,不用我们去遍历。
但是epoll只有linux支持。
网友评论