BIO:
多个请求并发时,会产生多个线程,单核cpu时,内核同一时刻只能处理1个线程,早期的socket是阻塞的(blockingIO-BIO)。线程之前的切换回消耗cpu资源,所以早期BIO时代,计算机硬件很难被利用起来。
产生的问题:计算机硬件很难被利用起来,于是产生了NIO时代。
NIO:
这个时候内核是可以非阻塞的处理socket请求,当socket变成了非阻塞的时候,就可以使用一个线程/进程轮询处理,此时的轮询过程发生在用户空间。这个时期是同步的,因为遍历和处理都是自己完成的,所以是同步非阻塞的(nonblockIO -NIO)。
产生的问题:如果有1000个fd,那么就会在用户空间就会轮询1000次调用内核,这样成本很大。
多路复用的NIO:
内核增加了系统调用select,此时的轮询发生在了内核空间,客户端线程调用select将1000个fd传给内核,内核空间完成轮询过程,再把轮询后的结果传回给用户空间,用户根据结果调用read指令。之前是调用1000次内核,现在1000个fd调用一次内核。减少了用户态和内核态的切换,这就是多路复用的NIO。
产生的问题:每次客户端向内核传1000个fd,然后内核将1000个结果集传回给客户端,然后客户端再根据结果取调用其他指令。这个过程很复杂,如何能减少过程中内核态和用户态之前关于fd的copy次数呢?
EPOLL:
增加了一个新的系统调用指令mmap并且在内存上开辟一个共享空间,这个共享空间属于用户空间也属于内核空间。共享空间中有红黑树和链表。用户态的线程/进程通过epoll讲1000个fds写到共享空间的红黑树中,内核从红黑树中取fds进行读取,将读取后的结果放入链表,然后用户态去链表中取结果。