以下只是鄙人的粗略理解,有错误地方请指正!
select模型
首先根据FD数组去初始化一个bitmap,bitmap也是一个数组,固定长度为1024,bitmap的下标对应一个fd的状态。
并且每次函数调用,用户态都要将一整个bitmap拷贝到内核态,由内核态去检查状态,然后再将其拷贝回用户态。在用户态又根据轮询的方式,判断哪个fd就绪了,然后进行IO操作。 频繁地拷贝一整个bitmap,开销非常大,由是无差别地轮询查找就绪fd,o(n)时间复杂度, 效率非常低 。有连接限制,简单容易实现。
poll模型
与select模型类似,也是轮询FD是否就绪。只不过是用一个结构体数组代替了select中的FD数组和bitmap,这个结构体记录着FD,注册的事件,实际发生的事件。将这个结构体数组拷贝到内核态,由内核态去更新FD结构体的事件状态,然后又拷贝回用户态。
epoll模型
epoll_create(),epoll_ctl(),epol_wait()
linux特有的,底层采用双向链表+红黑树存储FD集合,查找的时间复杂度是常数时间复杂度,并且将轮询改为了事件通知的方式,维护FD集合的数据结构直接存储在内核态,减少了反复的拷贝,性能开销大幅度减小。
执行流程 epoll_create()首先初始化一个eventpoll结构体,这个结构体包含了FD就绪队列,基于红黑树实现的FD集合,阻塞队列。
调用epoll_ctl()维护红黑树FD的集合。增删改这些。
epol_wait()当前进程进入阻塞队列。
当有一个FD有事件发生的时候,就会将其FD插入到就绪队列中,这个插入动作实际是一个回调,并不是遍历,这是epoll的一个性能提升非常大的一个点,并且内核态会检查阻塞队列的进程,其FD集合中是否存在已经就绪的FD,这一步就是在红黑树里面查找FD,时间复杂度接近o(1)效率非常高,如果存在唤醒该线程。