@
ssword如果只是说阻塞/非阻塞、同步/异步,恐怕还是不能讲得太清。
阻塞发生在什么地方,一般来说发生在应用层向传输层的数据交换上。我们send/recv,实际上是与传输层打交道。比如recv,我们是把传输层的读缓冲区的数据搬到用户缓存区中,如果读缓冲区不可读(比如没数据),那阻塞模式下用户程序会停在的recv处,直到读缓冲区有了数据可以读了为止。这里的阻塞与否主要是socket意义上的。总的来说,阻塞是个容易理解的概念。
而异步这个概念,比较泛,不好谈。最广义的异步可以通过同步来模拟,如果如果,则如何,否则如何,这其中的“则如何”不同需要关心何时会“如果”。多路复用一种很经典的程序结构,就是把线路给Selector,Selector来告诉用户程序那些线路可以进行IO。select/poll/epoll/kqueue都可以作为Selector。其中select因为是轮询的,效率较差。而epoll是基于事件通知的,效率不错。使用epoll,无论是多个进程,还是多个线程,理论上效能都一回事,实际和linux的进程、线程调度有关系,和用户程序质量也有关系。使用epoll,监视IO的可读写性的工作交给了系统,而真正的数据搬运,用户程序自己来。正因为如此,我们不能说epoll是AIO,两者基本扯不上关系。当然,epoll可以帮助编写一套广泛意义上的AIO套件,比如epoll等到读写事件,就把活交给一个IO线程去办,办完再通过该套件自己的事件机制向用户发通知说我办完了。虽然这样多扰了几层,但一定程度还是符合AIO的定义的。
说这些,是想说我对IO模型的理解并没有多少偏差。我的观察是,大多数人用epoll就实现了高性能并发的需求,Linux虽然实现了一系列aio_*,但没多少人去用。而AIO好像并非是性能的终极方案。