I/O多路复用的select、poll,以及epoll的区别

问题提出

在Linux2.6中,加入了新的api:epoll,对比以前使用select和poll,epoll的增加使得性能大大提升,这是如何实现的?epoll的工作模式有哪几种?其内部原理是什么?


相关

文件描述符:索引值,指向记录表,当程序打开或者创建一个文件时,内核向进程返回一个文件描述符。

在网络编程中,很多函数是阻塞的,利用IO复用可以用非阻塞的形式来执行代码。同时处理读写、监听多端口等等。

如何处理阻塞式IO?


阻塞式IO和非阻塞式IO、IO多路复用

阻塞式IO:调用某个IO操作,例如read等待接收数据并读取,但是如果数据没有收到,那么线程或者进程就会被挂起,直到接收到数据。阻塞,即一直等待,长时间处于等待状态的话,就成阻塞IO。

非阻塞式IO:在非阻塞模式的时候,调用read,如果没有接收到数据,就立刻返回错误信息,并不会长期的等待下去,但是需要不断的轮询来读取,尝试获取数据,如果有数据,则返回数据,没有数据的话返回错误继续轮询。

IO多路复用:用一个特定的线程来检查文件描述符fd的状态(例如调用select和poll),如果有一个fd是就绪状态,则继续下一步执行。可以理解为派代表来监听多个fd是否有数据到来,如果有数据则告诉进程需要进行处理。

对于阻塞式IO的解决方案
①使用多线程和多进程,会造成程序复杂,并且进程和线程的创建维护也需要开销。
②用一个进程,使用非拥塞IO,当一个IO不可读时立即返回检查下一个是否可读,轮询机制(polling)。浪费CPU时间,大多数时间是不可读
③异步IO,使用信号量,当一个描述符准备好的时候,通过信号量告诉进程。信号量个数有限,多个描述符时不适用
④IO多路复用,每个进程/线程同时处理多个连接


select、poll

select:在读取文件描述符fd之前,先检查其状态,当文件数据准备好的时候再进行处理。用fd_set结构来通知内核同时监控多个fd,当状态改变的时候或超时,调用返回,之后的程序使用FD_ISSET来逐个检查是哪些文件描述符的状态发生了变化。当连接数很多,fd较大的时候,逐个检查状态非常慢。所以select存在fd的管理上限。同时还需要字段来记录关注事件和发生事件,但是只有一个字段,所以每次调用需要初始化。
当有连接请求的时候再检查处理。

缺点:fd管理有上限、逐个排查效率低、状态字段重复初始化。

poll:解决了select中对fd管理的上限问题,通过特定的数组向内核传递需要被关注的事件。使用不同字段记录关注事件和发生事件,避免重复初始化。但是仍然需要逐个排查fd的状态。


epoll

epoll:在调用返回的时候,只给程序返回发生了状态变化的fd,解决了逐个排查效率低的问题

epoll的工作模式
①EPOLLLT,LT(Level Triggered)模式是默认的,内核告知用户一个fd已经准备好,那就可以对就绪的fd进行IO操作。如果不需要任何操作,内核仍然会通知用户。

②EPOLLET,ET(Edge Triggered)模式是“高速”模式,当描述符变为就绪状态时,内核会告知用户已就绪,并假设用户已经知道该状态,不会再对其发送更多的通知。直到该文件描述符因为其他操作不再是就绪状态。如果一直未对该fd作IO操作的话,内核也不会再次通知。

ET模式的优点,如果系统中有大量不需要读写的就绪的fd,LT模式会不断的通知用户,降低对其他需要处理的fd的检索效率。

epoll的优点
①没有最大并发连接的限制,能打开的是fd数量远大于1024(select只能最多同时监听1024个fd)。
②效率提升,不是轮询的方式,不会随着fd数量的增加而降低效率。epoll只关心活跃的连接,与连接总数无关。
③用文件映射内存mmap(),加速与内核的消息传递,通过内核和用户空间共享一块内存来实现。(select内核要将消息传递到用户空间,需要拷贝操作)

你可能感兴趣的:(计算机系统,网络,Linux/Unix,计算机应用)