IO 模型
同步/异步:用户线程和内核的交互方式。
阻塞/非阻塞:用户线程调用内核IO操作的方式。
概念 | 描述 |
---|---|
同步 | 用户线程发送IO请求后,需要等待或者轮询内核IO操作完成后,才能继续执行。 |
异步 | 用户线程发送IO请求后,仍继续执行。当内核IO操作完成后会通知用户线程,或调用用户线程注册的回调函数。 |
阻塞 | IO操作完成后,才会返回用户空间。 |
非阻塞 | IO操作被调用后立即返回用户状态值。 |
Reactor 模型
Proactor 模型
文件描述符(fd)
是 File descriptor 的缩写,中文名叫做:文件描述符。文件描述符是一个非负整数,本质上是一个索引值。
file_struct 本质上是用来管理所有打开的文件的,内部的核心是由一个静态数组和动态数组管理结构实现。
struct file 是属于系统级别的结构,换句话说是可以共享与多个不同的进程。
父进程打开了文件,后面 fork 出一个子进程。这种情况就会出现共享 file 的场景。
系统调用
通过红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效。
- epollcreate 负责创建一个池子,一个监控和管理句柄 fd 的池子;
- epollctl 负责管理这个池子里的 fd 增、删、改(
红黑树
); - epollwait 就是负责打盹的,让出 CPU 调度,但是只要有“事”,立马会从这里唤醒;
poll接口
poll是Linux的事件轮询机制函数
,每个进程都可以管理一个pollfd队列,由poll函数进行事件注册和查询。
通过 poll 机制让上层能直接告诉底层,我这个 fd 一旦读写就绪了,请底层硬件(比如网卡)回调的时候自动把这个 fd 相关的结构体放到指定队列中,并且唤醒操作系统。
中断一般拆分成两部分:硬中断和软中断。poll 函数就是把这个软中断
回来的路上再加点料,只要读写事件触发的时候,就会立马通知到上层,采用这种事件通知的形式就能把浪费的时间窗就完全消失了。
就绪队列
就是一个最简单的双指针链表
。
- ext2,ext4,xfs 等这种真正的文件系统的 fd ,无法使用 epoll 管理;
- socket fd,eventfd,timerfd 这些实现了 poll 调用的可以放到 epoll 池进行管理;
epoll的工作模式
-
ET(EdgeTriggered):高速工作模式,只支持no_block(非阻塞模式)。在此模式下,当描述符从未就绪变为就绪时,内核通过epoll告知。然后它会假设用户知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到某些操作导致那个文件描述符不再为就绪状态了。(触发模式只在数据就绪时
通知一次
,若数据没有读完,下一次不会通知,直到有新的就绪数据) -
LT(LevelTriggered):缺省工作方式,支持blocksocket和no_blocksocket。在LT模式下内核会告知一个文件描述符是否就绪了,然后可以对这个就绪的fd进行IO操作。如果不作任何操作,内核还是会
继续通知
!若数据没有读完,内核也会继续通知,直至设备数据为空为止!
参考
epoll
golang 利用 epoll
网络模型
Reactor & Proactor
Reactor & Proactor
ET<