同步、异步和阻塞、非阻塞之间的关系
什么是同步I/O? 什么是异步I/O?
-
同步(阻塞)I/O
在一个线程当中,CPU的执行速度极快,然而一旦遇到I/O操作,如读写文件,发送网络数据是,就需要等待IO操作完成,才能继续下一步操作。这种情况成为异步IO。
-
异步(非阻塞)I/O
当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果是,再通知CPU进行处理。
同步才有阻塞,异步没有阻塞!
Linux下有三种I/O复用方式:select,poll,epoll,三者的区别是什么?
- 对于select和poll来说,所有的文件描述符都是在用户态被加入文件描述符集合的,每次调用都需要将整个集合拷贝到内核态;epoll则将整个文件描述符集合都维护在内核态,每次添加文件描述符的时候都需要执行一个系统调用。系统调用的开销是很大的,而且有很多短期活跃连接的情况下,epoll需要频繁的进行系统调用,因此可能会慢于select和poll。
- select使用线性表描述文件描述符集合,文件描述符有上限;poll使用链表来描述;epoll底层是通过红黑树来描述的,并且维护一个ready_list,将事件表中已经就绪的事件添加到list中,在使用epoll_wait调用时,仅观察这个list中有没有数据即可。
- select和poll最大的开销来源于判断是否有文件描述符就绪这一过程;每次执行select或者poll 的时候,他们会采用遍历的方式,遍历整个文件描述符集合去判断各个文件描述符是否有活动;epoll则不需要进行这些检查,当有活动产生的时候,会自动触发epoll回调函数同志epoll文件描述符,然后内核就将这些就绪的文件描述符放到之前提到的ready_list 中,等待epoll_wait调用后被处理。
- select和poll只能工作在相对低效的LT模式下,而epoll同时支持LT和ET模式。
- 综上所述,当检测的fd数量较少,且各个fd都很活跃的情况下,建议使用select或者poll;当监听的fd数量较多,且单位时间仅部分fd活动的情况下,使用epoll的效果会更好。
epoll对文件描述符的操作有两种模式:LT(电平触发)模式和ET(边缘触发)模式,二者的区别在于你调用epoll_wait 的时候内核里面发生了什么。
-
LT(电平触发):
类似于select,LT回去遍历在epoll事件表中的每个文件描述符,看是否有我们关注的事情发生,如果有(触发该文件描述符上的回调函数),epoll_wait就会以非阻塞的方式返回。若改epoll时间没有被处理完(没有返回EWOULDBLOCK),该事件还会被后续的epoll_wait再次出发,直到读写完毕。
-
ET(边沿触发):
ET在发现有我们关注的事情发生之后,会立即返回,并且sleep这一事件的epoll_wait,不管该事件有没有结束。
使用ET模式是,必须要保证文件描述符是非阻塞的(确保在没有数据可读是,该文件描述符不会一直阻塞);并且每次调用read和write的时候都必须等到他们返回EWOULDBLOCK(确保所有的数据都以读写完成)。