select poll epoll

IO是怎样实现的?
IO这个操作普通平凡,可是原理复杂,是由操作系统完成的。所以IO操作必然要准换到内核态执行。

IO的成本在哪里?

  1. Waiting for the data to be ready - 等待数据准备好
  2. Copying the data from the kernel to the process - 将数据从内核空间的buffer拷贝到用户空间进程的buffer

这两个步骤都是在OS内核中完成的。

select、poll、epoll是什么?
他们都是IO多路复用机制,可以监视多个描述符的读写事件,一旦某个描述符就绪(一般发生了读或写事件),OS就能将发生的事件通知给服务端程序去处理。


他们都是同步的,但优势是用很少的代价监听IO事件。
监听这个动作是谁在做?当然是OS内核。这个过程是同步的,服务端程序会阻塞。
select:当有事件发生时,OS内核会通知服务端程序,但服务端程序不知道具体是哪个socket就绪,所以只能遍历描述符列表。
select的问题
(1)被监控的fds集合限制为1024
(2)fds集合需要从用户空间拷贝到内核空间,我们希望不需要拷贝
(3)当被监控的fds中某些有数据可读的时候,我们希望通知更加精细一点,就是我们希望能够从通知中得到有可读事件的fds列表,而不是需要遍历整个fds来收集。

poll:鸡肋。只解决了问题(1)

epoll:当有事件发生时,OS内核会通知服务端程序,会把发生事件的描述符列表告诉服务端程序。
对于问题(2),epoll通过内核与用户空间mmap(内存映射)同一块内存来解决。mmap将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址。这块物理内存对内核和对用户均可见,减少用户态和内核态之间的数据交换。
epoll分为两种模式

  • 水平触发:Level Triggered (LT) (默认方式)(redis使用)
    只要读缓冲区未空或者写缓冲区未满,则会触发。
    内核通知某个fd就绪,如果不做任何操作,内核还是会不断通知。

  • 边沿触发:Edge Triggered (ET) (Ngnix使用)
    如果文件描述符自上次状态检查以来有了新的I/O活动,此时需要触发通 知。

举个例子:一个管道内收到了数据,注册该管道描述符的epoll返回,但是用户只读取了一部分数据,然后再次调用了epoll。这时,如果是水平触发方式,epoll将立刻返回,因为当前有数据可读,满足IO就绪的要求;但是如果是边沿触发方式,epoll不会返回,因为调用之后还没有新的IO事件发生,直到有新的数据到来,epoll才会返回,用户可以一并读到老的数据和新的数据。

select()和poll()属于水平触发通知模式。
epoll即可以采用水平触发通知方式,也可以采用边缘触发通知方式。

参考:
大话 Select、Poll、Epoll
Java BIO & NIO
Linux IO模型

你可能感兴趣的:(select poll epoll)