什么是io多路复用
单线程或者单进程同时检测多个文件描述符是否可以执行io操作的能力了
解决什么问题
应用程序往往要处理多条事件流的事件,而cpu单核在同一时间只能做一件事,一种解决方式就是将cpu进行时分复用。在计算机系统中,我们用线程和进程来表示一条执行流,通过不同的线程和进程在操作系统内部的调度,来做到对cpu的时分复用。
线程/进程有几个成本
- 线程/进程创建成本
- cpu切换不同的线程/进程成本
- 多线程的资源竞争
io多路复用的解决本质就是用更少的资源完成更多的事
linux的io处理模型
- 阻塞io
- 非阻塞io
- io多路复用
- 信号驱动io
- 异步io
阻塞io
这是最常用的简单的io模型。阻塞io意味着当我们发起一次io操作后,要一直等待成功或者失败后才返回,在这期间程序不能做任何其他的事情。阻塞io只能对单个文件描述符进行操作,如read和write。
非阻塞io
我们发起io时,通过对文件描述符设置O_NONBLOCKE flag来指定该文件描述符的io操作为非阻塞。非阻塞io通常发生在一个for虚幻中,,因为每次进行io操作时,要么io操作成功,要不io操作会阻塞时返回错误EWOULDBLOCK/EAGAIN,然后再根据需要进行下一次for循环操作,这种类似轮询的方式会浪费很多不必要的cpu资源。和阻塞io一样,非阻塞io也是通过read和write来进行操作的,也只能对单个描述符进行操作
IO多路复用
有三种方式selelct/poll/epoll,他们的共同点:首先都会对一组文件描述符进行相关事件的注册,然后阻塞以等待某些事件发生或者等待超时。对于三种机制而言,不同数量的文件描述符对性能的影响是不同的
信号驱动io
信号驱动io是利用信号机制,让内核告诉应用程序文件描述符的相关事件。但在网络环境中 ,和socket相关的读写事件太多了,我们没有办法在sigio对应的信号处理函数中区分不同的事件,sigio只能在io事件单一的情况下使用,比如监听端口的socket,因为只有客户端发起新的链接的时候才会产生sigio信号
异步io
异步io和信号驱动io差不多,不一样的地方是:它相比较信号驱动io需要在程序中完成数据从用户态到内核态(或者反方向)的copy,异步io把copy这一步完成后,再通知应用程序。我们使用的事aio_read和aio_write
同步io和异步io
- 同步io指的是程序会一直阻塞程序到io操作如read或者write完成
- 异步io指的是哦操作不会当前程序的继续执行
io多路复用的方案
select
select的方法有select pselect FD_CLR FD_ISSET FD_SET FD_ZERO
select的调用会阻塞到有文件描述符可以进行io操作或者被信号打断或者超时才会返回
select将监听三种不同的需要进行的io操作。readfds是需要进行读操作的文件描述符,writefds是需要进行写操作的文件描述符,exceptfds是需要进行异常处理的文件描述符。这三个参数通过返回null来表示不需要监听
当select返回的时候,都会进行select过滤,只会留下需要进行io的文件描述符
FD_XX 系列的函数是用来操作文件描述符组合文件描述符的关系
FD_ZERO 用来清空文件描述符组,每次调用select都需要清空一次
FD_SET 用来将一个文件添加到文件描述符组里
FD_CLR 用来将一个文件描述符移除文件描述符组
FD_ISSET 检测一个文件描述符是否在组中,用来检测那些文件描述符可以进行io操作
select 可同时监听的文件描述符数量可以通过FS_SETSIZE来限制的,在linux中是1024,但是是可以设置的,但随着这个数的增加,效率会降低.pselect 和select 大体是一样的,有些细节上的区别
poll
poll的函数有 poll/ppoll/pollfd(fd, events, revents)
和select的三组文件描述符组不同的是,poll只有一个pollfd数组,每个元素都表示一个需要监听io操作事件的文件描述符。events是我们需要关心的事件,revents是内核检测到的事件。
epoll
epoll的函数有 epoll_create/epoll_create1/epoll_ctl/epoll_wait/epoll_pwait
epoll_create/epoll_create1 用来创建一个epoll实例,epoll_ctl用来添删改要检测的文件描述符,epoll_wait用来阻塞等待可以执行的io直到超时
level-triggerd and edge-triggerd
level-triggerd表示只要有io操作可以进行,比如某个文件描述符有数据可读,每次调用epoll_wait都会通知程序可以进行io操作
edge-triggerd表示只有文件描述符发生状态变化的时候,调用epoll_wait,会通知应用程序可以进行io操作,如果第一次没有全部读完数据,第二次没有新数据进入,则不会返回可读。
select和poll都是level-trigger , epoll是两种可选
原文
https://gocn.vip/topics/10090