开发者学堂课程【Nginx 企业级 Web 服务实战:IO 模型介绍】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/649/detail/10751
IO 模型介绍(三)
八、常用模型汇总:
Select |
Poll |
Epoll |
|
操作方式 |
遍历 |
遍历 |
回调 |
底层实现 |
数组 |
链表 |
哈希表 |
IO 效率 |
每次调用都进行线性遍历,时间复杂为0(n) |
每次调用都进行线性遍历,时间复杂度为0(n) |
事件通知方式,每当 fd 就绪,系统注册的回调函数就会被调用,将就绪 fd 放到 rdllist 里面。时间复杂度 0(1) |
最大连接数 |
1024(x86)或 2048(x64) |
无上限 |
无上限 |
Fd 拷贝 |
每次调用 select,都需要把 fd 集合从用户态拷贝到内核态 |
用户态拷贝到内核态每次调用 poll ,都需要把 fd 集合 |
调用 epoll_ctl 时拷贝进内核并保存,之后每次 epoll_wait 不拷贝 |
九、常用模型对比
1.水平触发--多次通知,需要关心数据是否取完以避免重复通知,效率较低。
2.边缘触发--一次通知,需要关心数据是否取走以避免数据丢失,效率较高。
Select:
POSIX 所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理
缺点:
单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为1024,可以通过修改宏定义 FD SETSIZE,再重新编译内核实现,但是这样也会造成效率的降低单个进程可监视的 fd 数量被限制,默认是1024,修改此值需要重新编译内核对 socket 是线性扫描.即采用轮询的方法,效率较低。
select
采取了内存拷贝方法来实现内核将 FD 消息通知给用户空间,这样一个用来存放大量 fd 的数据结构,这样会使得用卢空间和内核空间在传递该结构时复制开销大
poll:
本质上和 select 没有区别,它将用卢传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态其没有最大连接数的限制,原因是它是基于链表来存储的大量的fd的数组被整体复制于用巨态和内核地址空间之间,而不管这样的复制是不是有意义 poll 特点是“水平触发”,
如果报告了 fd 后,没有被处理,那么下次 poll 时会再次报告该 fd
epoll:
在 Linux 2.6 内核中提出的 select 和 poll 的增强版本,支持水平触发 LT 和边缘触发 ET,最大的特点在于边缘触发,它只告诉进程哪些 fd 刚刚变为就需态,并且只会通知一次 e 使用“事件”的就绪通知方式,通过 epoll ctl 注册 fd,一旦该fd就绪,内核就会采用类似 callback 的回调机制来激 < 活该 fd,epoll wait 便可以收到通知
优点:
没有最大并发连接的限制:能打开的FD的上限远大于1024(1G 的内存能监听约10万个端口),具体查看 /proclsvs/fs/file-max,此值和系统内存大小相关
效率提升:
非轮询的方式,不会随着 FD 数目的增加而效率下降:只有活跃可用的 FD 才会调用 callback 函数,即 epoll 最大的优点就在于它只管理“活跃”的连接,而跟连接总数无关
内存拷贝,利用 mmap(Memory Mapping) 加速与内核空间的消息传递:即 epoll 使用 mmap 减少复制开销
十、MMAP 介绍:
mmap(memory mapping) 系统调用使得进程之间通过映射同⼀个普通文件实现共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存⼀样对文件进行访问。
十一、传统方式 copy 数据:
传统方式下需要进程去调用内核,内核从磁盘读取数据,读到内存的内核空间,再把数据拷贝到进程的内核上,一个进程上有一个独立的储存空间。可以理解为先从磁盘拷到内存的内存空间,再从内存拷到进程的内存空间,需要拷贝两次。
十二、Mmap方式:
使用mmap方式,先从磁盘拷到内存空间,这一步是必须的。将进程的内存空间映射到内存的内存地址,让进程直接访问内存中的数据,这种方式被成为mmap。
这样做的优点是可以减少一次数据拷贝,从而加快一次数据的传输时间。即从磁盘考到内存的内存空间,然后映射到进程空间。通过这个软件就可以找到数据,然后直接打包数据发给用户。
不再从内核内存拷贝到进程内存,可以直接访问,这一步也是由内核实现的。