假设client已经连接到server,那么在client发出read调用时,分为两个阶段的拷贝
那么对于两阶段拷贝,根据不同的IO类型,有如下情况
阻塞
client发起阻塞read调用后,如果内核缓冲区为空,则会一直等待到网卡缓冲区拷贝数据到内核
非阻塞
client发出非阻塞read调用后,如果内核缓冲区为空,则会立即返回,不会等待网卡缓冲区的数据拷贝到内核
同步
那么client会一直阻塞等待内核缓冲区数据拷贝到用户缓冲区
异步
那么client会为此事件保留一个callback,不会发生阻塞,当数据拷贝到用户空间后,调用callback通知client来开始读取传来的数据
下面列举一些实际的IO
阻塞read调用
同步阻塞
非阻塞read调用
同步非阻塞
IO多路复用
从用户角度看
阻塞IO,由于发起select等调用会一直阻塞到有socket返回,一般为同步
从内核角度看
非阻塞IO,由于内核调用的是非阻塞的read调用,检查哪些socket就绪,一般为同步
信号驱动IO
调用时向内核注册一个回调,当数据拷贝到内核空间时返回,一般为同步
异步IO
在调用后注册回调并返回,直到数据拷贝到用户空间触发回调,也即在两个阶段均不阻塞
使用accpet监听,将返回的socket放入队列,之后创建线程周期性的发起非阻塞调用,检查哪些socket就绪
将1)中用户态执行的轮询检查放入内核态执行
流程如下
将进程挂载到fd的wait-queue
将fd放入到用户态监听队列
用户态监听队列拷贝到内核监听队列
内核轮询监听队列,检查是否有就绪的fd
如果有,则返回就绪的个数,唤醒调用进程
进程轮询用户态监听队列,找出其中就绪的fd,进行处理
可见,进程只能得到就绪的fd的个数,不知道具体哪些fd就绪
类似于select,采用链表方式构建等待队列,去掉了select中1024的监听队列限制
epoll时一个存在于内核的结构体,其包含三个关键成员
rbr
红黑树,用于快速插入、查询、删除fd
rdlist
就绪队列,所有就绪的socket会被注册到这里
wait-queue
等待队列,用于触发回调
epoll执行有三个关键函数
epoll_create
创建epoll对象
epoll_ctl
创建epitem并插入epoll对象中的红黑树中
epoll_wait
检查epoll中的rdlist是否为空,如果为空则阻塞,不为空则返回
那么,一般的执行流程如下
epoll_create创建epoll对象,将其挂载到进程的fdlist中
调用epoll_ctl,创建epitem,将(epitem,ep_poll_callback)插入socket的waitqueue中,并将epitem插入rbr
发起epoll_wait,首次检查发现rdlist为空,创建(proc,default_wake_function)放入到epoll的wait-queue中
进程开始阻塞
......
当数据抵达网卡时
socket被唤醒,之后检查waitqueue,调用ep_poll_callback,
epoll_wait返回可用的fd的个数,并将rdlist拷贝到用户空间
被唤醒的进程遍历用户空间的rdlist,分别进行处理
可见,进程不仅可以得到就绪的fd个数,还是直到是哪些fd就绪
epoll对于发送、接收内核缓冲区的状态检查,有两种触发方式
水平
读操作
只要接收缓冲区不空,就一直触发
写操作
只要发送缓冲区不满,就一直触发
边沿
读操作
当接受缓冲区收到数据时,只触发一次,如果未读完,则不会再次触发
写操作
当发送缓冲区空出空间时,只触发一次
那么在此状态下,只要可读/写,就要一直读/写,直到返回EAGAIN错误(读缓冲区无数据可读/发缓冲区无空间可写)
Java的NIO中的selector即Reactor模式中的多路选择(解复用)器,用户进程需要将socket(channel)和对应的事件注册到selector上,之后selector所在线程调用select()
,其会阻塞到注册的事件中至少有一个被激活,当select()
返回之后调用selectedKeys()
用于返回所有的就绪事件对应的key,之后用户进程只需要遍历返回的就绪时间队列,判断对应的事件类型,执行对应的方法即可(如read返回,则开始读数据)
redis中由于采用单线程模型,为了应对并发的IO请求,同样采用IO多路复用来处理,将收到的任务交由文件事件分派器来处理
Epoll在LT和ET模式下的读写方式 - 平凡的世界 (kimi.it)
Redis 到底是单线程还是多线程?我要吊打面试官! - Java技术栈 - 博客园 (cnblogs.com)
图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!
大话 Select、Poll、Epoll - 云+社区 - 腾讯云 (tencent.com)
原文:https://www.cnblogs.com/lins1/p/15229526.html