-
内核态与用户态
-
内核态: 可以访问系统资源, 比如CPU, 内存, 网络, 外设
-
用户态: 只能访问进程自己的资源, 无法访问系统资源
-
用户态需要访问系统资源时, 需要CPU切到内核态, 读取资源后再切回用户态. 中间涉及堆栈上下文的切换, 为避免频繁切换, 有了"用户缓冲区"和"系统缓冲区".
-
当用户进程需要从"磁盘/网络"中读取数据时, 系统会将"系统缓冲区"的数据复制到"用户缓冲区". 若"系统缓冲区"中没有对应数据, 系统会将当前进程挂起, 处理其他进程. 等数据到达"系统缓冲区"后, 系统将数据拷贝至"用户缓冲区", 然后才会通知进程, 注意不同IO模型方式不同.
-
-
五种IO模型, IO即磁盘/网络读写
-
c10k 一台计算机实现10000台客户端的连接, 并处理对应的请求. 一个进程处理一个连接, 会消耗过多资源. 所以要一个进程处理多个连接, 即IO多路复用, 目前通过IO多路复用已实现c10k.
-
五种IO模型 (参考自https://zhuanlan.zhihu.com/p/54580385)
-
所有的IO模型都分为两阶段, 前四个模型, 第二阶段复制数据阶段都是阻塞的.
-
等待系统将数据准备好, 即等待数据从网卡/磁盘复制到"系统缓冲区"
-
将数据从"系统缓冲区"复制到"用户缓冲区"
-
-
阻塞式IO, 第一阶段"请求进程"调用receive, 一直阻塞至第二阶段完成
-
非阻塞IO, 第一阶段"请求进程"不停调用receive, receive立即返回异常. 直至"数据已备好"后, "请求进程"再调用receive会阻塞至第二阶段完成
-
IO多路复用(即事件驱动), 第一阶段"请求进程"调用select, 并阻塞至"数据准备好". 第二阶段"请求进程"发起receive, 阻塞至拷贝完成. 虽然看起来"请求进程"都是阻塞, 但是服务器内核可以同时监听select负责的多个套接字, 服务器效率非常高
-
信号驱动IO, 第一阶段"请求进程"向内核注册信号后, 不阻塞. 当"数据准备好"后, 内核通过信号通知"请求进程", "请求进程"调用receive, 阻塞取得数据
-
异步IO, 第一阶段"请求进程"发起receive, 不阻塞. 当第二阶段数据拷贝完后, 内核会通过回调通知"请求进程".
-
-
nginx是事件驱动服务器的代表, 适合IO密集型服务, 使用的是epoll; Apache是线程服务器的代表, 适合计算密集型服务(apache存疑)
-
select, poll, epoll
-
-
零拷贝 (参考自https://juejin.im/post/6844903949359644680)
-
当用户进程通过网络发送磁盘数据时, 依次调用read和send.
-
调用read时, 首先内核会检查"系统缓冲区"有无缓存, 若没有, 会通过DMA将数据从磁盘拷贝到"系统缓冲区". 然后通过CPU将数据从"系统缓冲区"拷贝到"用户缓冲区".
-
调用send时, 通过CPU将数据从"用户缓冲区"拷贝到套接字的"系统缓冲区", 再由套接字的"系统缓冲区", 通过DMA拷贝到网卡中, 发送出去.
-
总体上经过4次内核态与用户态的上下文切换, 2次CPU调用, 2次DMA调用
-
-
零拷贝优点
-
减少数据在"用户缓冲区"与"系统缓冲区"间的拷贝
-
减少数据拷贝时, "用户态"与"内核态"的上下文切换
-
-
实现方式
-
用mmap+send代替的read+send, mmap可将指定的"系统缓冲区"对用户进程共享, 从而避免CPU将数据从"系统缓冲区"拷贝到"用户缓冲区", 因此也不存在send时CPU将数据再从"用户缓冲区"拷贝到"系统缓冲区", 但会多1次从"系统缓冲区"到套接字的"系统缓冲区"的CPU拷贝, 故整体少了1次CPU拷贝. 总体上4次上下文切换, 1次CPU调用, 2次DMA调用
-
用sendfile代替read+send, 也是减少1次CPU拷贝, 但因为数据可以直接在内核空间传输, 故减少2次上下文切换. 总体上2次上下文切换, 1次CPU调用, 2次DMA调用
-
用splice代替read+send, 通过在"系统缓冲区"与套接字的"系统缓冲区"间建立管道, 完全避免了CPU拷贝. 总体上2次上下文切换, 0次CPU调用, 2次DMA调用
-
-
「操作系统」五种IO模型 零拷贝
最新推荐文章于 2023-03-15 20:06:14 发布