IO模型
https://zhuanlan.zhihu.com/p/115912936
https://zhuanlan.zhihu.com/p/439770090
https://blog.csdn.net/u014453898/article/details/109811000
https://cloud.tencent.com/developer/article/2141900
https://zhuanlan.zhihu.com/p/614204046
进行一次IO的时候(非零拷贝),是需要经过两个阶段的,分别是
- 第一阶段:等待内核把数据准备,即等待数据搞好
- 第二阶段:把数据拷贝到用户态
从操作系统来看,NIO指的是非阻塞IO,而从Java来看,NIO指的是IO多路复用
阻塞IO
应用程序向内核发起IO请求,第一阶段也阻塞,第二阶段也阻塞,直到数据从内核空间拷贝到用户空间
非阻塞IO(NIO)
应用 程序向内核发起IO请求,第一阶段不会阻塞,会返回去干别的事情,然后定时轮询,如果某次询问到内核数据准备好了。那么这时候就会直接等待数据从内核态拷贝到用户态
即第一节点不阻塞,第二阶段不阻塞
IO多路复用
应用程序向内核发起请求,开始阻塞监听多个请求,如果内核收到数据了,就会主动来告诉应用程序说数据好了,然后应用程序向内核发起数据请求,等待数据从内核态拷贝到应用态。
IO多路复用分为三种
- select:底层采用数组,最多监听1024个fd,即1024个数据来源。会将已连接的socket存放到文件描述符集合中。然后for(Socket socket:Sockets){ if(socket.isOk()) {开始处理事件}} 。如果for循环之后没有满足条件的Socket,那么内核将进行休眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的内核进程,即在socket可读写时唤醒,或者在超时后唤醒。当检测有事件产生时,将此Socket标记为可读可写,接着将整个集合拷贝回用户态,应用程序再对其进行遍历找到可读or可写的Socket,然后对其进行处理。处理完数据后继续调用select对其进行监听,而此时又要重新设置一遍集合(读,写,异常事件集合,三个集合)
- 这里两次遍历,一次用户态,一次内核态。还有两次文件描述符的拷贝,拷贝来 以及拷贝回去
- poll 底层采用的是链表,没有连接数的限制,将实际发生的事件与关心的事件分开,但是呢没有解决遍历的问题,处理好数据后重置fd对应的revent即可

- epoll,底层采用红黑树,可以准确通知应用程序,不需要像poll和select一样遍历了。调用一次epoll_ctl就好了,它会注册一个fd,一旦这个fd就绪的话,内核会采取回调机制,迅速去激活这个fd,当进程调用epoll_wati时就会得到通知


- 通过epoll_create创建epoll对象,此时epoll对象的内核结构包含就绪链表和红黑树,就绪队列是用于保存所有读写事件到来的socket。红黑树用于保存所有待检测的socket。
- 通过 **epoll_ctl **将待检测的socket,加入到红黑树中,并注册一个事件回调函数,当有事件到来的之后,会调用这个回调函数,进而通知到 epoll 对象。
- 调用 epoll_wait 等待事件的发生,当内核检测到事件发生后,调用该socket注册的回调函数,执行回调函数就能找到socket对应的epoll对象,然后会将事件加入到epoll对象的绪队列中,最后将就绪队列(其实是epoll_wait的时候传给内核态的)返回给应用层。
- 水平触发LT:有fd就绪时,重复通知直到数据处理完成,默认方式 (Redis采用的方式)
- 边缘触发ET:有fd就绪时,通知一次,直到下次有新的数据来,才会再次通知
第一阶段阻塞,第二阶段也阻塞,但是相比于阻塞IO就是能同时监听多个Socket,仍然会有阻塞现象,如果某个请求卡壳,会影响其他请求(单个selector的情况下)
信号驱动IO
应用程序向内核发送信号,然后就可以去做别的事,内核如果有数据,调用信号通知应用,应用程序收到信号后,向内核请求数据,等待内核将数据拷贝到用户空间完成,返回,处理数据。
第一阶段不阻塞,第二阶段阻塞,相比于NIO,不需要主动去轮询去问,而是由内核主动通知
异步IO(第二和第一都不阻塞)
应用程序向内核发送信号,然后就可以去做别的事,内核如果有数据,先将数据拷贝到用户空间,然后调用信号通知应用,应用程序收到信号后,可以直接处理数据!
相比于信号驱动IO,第二阶段不阻塞了,但现在技术还不是很成熟,支持的AIO的操作系统和框架也不多
传统IO模型
一对一(一个连接一个线程/进程)
每一个请求到来时,大致都会按照:请求读取->请求解码->服务执行->编码响应->发送答复 这个流程去处理。

Reactor模型
Reactor:负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含连接建立就绪、读就绪、写就绪等。
Acceptor:处理连接事件
Handler:执行非阻塞读/写事件
单reactor单线程

单reactor多线程

主从reactor多线程
主:负责创建连接请求
从:负责处理业务请求
