背景
单线程监听端口+读写socket+处理数据,如果接收了很多请求,每个请求状态不一样,处理起来就会非常复杂。
IO多路复用就是解决的这个问题,单线程可以把监听端口与监听请求状态的事情委托给IO多路复用模型,有消息的时候,模型再通知线程处理,同时告知线程这个消息的类型。
所述模型均由操作系统实现。
select
最早的一种IO多路复用模型
基本逻辑
1.主线程维护一个fd_set,fd_set里包含需要监听的fd,形式为bitmap,内存连续的线性表
2.每次主线程将fd_set交给select函数,select函数将fd_set从用户态拷贝进内核态,并监听
3.超时/用户设置立即返回/有消息的时候,select函数将内核态fd_set拷贝进用户态,并返回给主线程
4.主线程遍历fd_set,找到有消息的fd,处理消息
存在的问题
1.很明显,如果我的fd_set巨大,但有消息的fd很少,我就花了好多时间在遍历fd_set上
2.同样的,如果我很频繁调用select,但每次有消息的fd很少,fd_set就会反复在用户态与内核态之间转换
3.监听的socket数量有1024个的硬限制,修改限制需要重新编译os内核
4.由于select函数直接修改传入的fd_set,所以我们还要自己记一下现在手上有哪些fd,比较麻烦
优点
跨平台
poll
select改进版,但没啥太大进步
基本逻辑
1.主线程维护一个struct pollfd数组,数组里包含需要监听的fd、监听读or写or异常、监听结果
2.调用poll的时候,数组被拷贝进内核态,并且转化为walk链表。(链表可以在拷贝回用户态时只拷贝有变动的节点,减少拷贝量)
其他的与select类似,等于就是解决了select的问题4
存在的问题
1.同select的问题1与问题2
2.不跨平台,Linux实现
优点
1.有个数据结构存储fd(解决了select问题4)
2.能改socket文件数量上限