IO模型
IO模型须知
什么是IO(input/output)
- 网络IO
- 设备IO
IO模型的两个阶段
- 第一阶段:等待数据准备,此阶段是不需要占用CPU的,CPU通过调用DMA执行设备操作。可是如果是调用其他进程执行数据准备(例如mysql进程),还是会需要用到CPU,但是已经算是另外一个进程的IO操作了
- 第二阶段:数据从内核空间拷贝到用户空间,此阶段需要占用CPU进行拷贝
5种IO模型
- 阻塞IO
- 非阻塞IO
- 多路复用IO
- 异步IO
- 信号驱动IO
为何一个监听socket可以accept多次
accept()
能够返回一个新的socket。下面是 accept 接口的原型:
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
输入参数s是从socket()
,bind()
和listen()
中沿用下来的socket句柄值(int(fd))。执行完bind()
和listen()
后,操作系统已经开始在指定的端口处监听所有的连接请求,如果有请求,则将该连接请求加入请求队列。调用accept()
接口正是从 socket s 的请求队列抽取第一个连接信息,创建一个与s同类的新的socket返回句柄。新的socket句柄即是后续read()
和recv()
的输入参数。如果请求队列当前没有请求,则accept()
将进入阻塞状态直到有请求进入队列。
什么是文件描述符fd(File Descriptor),它是计算机科学中的一个术语,形式上是一个非负整数。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符
IO模型解析
阻塞IO (blocking IO)
阻塞只是针对于数据准备阶段(第一阶段)的用户进程(recvfrom),数据从内核拷贝到应用内存时依旧是阻塞态。当kernel没有获取到完整的数据时,用户进程会进入**阻塞态
**,所以这CPU使用权被让出,在此期间,线程将无法执行任何运算或响应任何的网络请求。当kernel准备好数据,cpu被调度回来执行数据拷贝。
因为阻塞状态,cpu使用权被让出,如果是多个socket连接,都在同一个进程上,一样没有cpu使用权,所以阻塞IO模型是一对一的。
blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。
阻塞态:
当用户进程阻塞后,进程进入等待,所以会让出CPU,CPU去执行其他的进程。
阻塞IO code
一对一阻塞IO
多线程阻塞IO
这虽然已经解决了多监听不阻塞,可是当成千上万个请求洪峰到来时,系统资源就会被占满,所以不是最佳方案
非阻塞IO(non-blocking IO)
非阻塞只是针对于数据准备阶段(第一阶段)的用户进程(recvfrom),数据从内核拷贝到应用内存时依旧是阻塞态。当用户发出read操作时,如果kernal没有准备好数据,recvfrom请求会立即放返回数据,并不会使用户进程blocking。
在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有。
非阻塞IO就实现了多个线程阻塞IO,因为不是阻塞态,所以进程还有CPU使用权,那么多客户端连接时,用户进程就会为下一个客户端的socket发送recvfrom请求,同时检测多个连接的数据时候准备就绪。一对多
DMA:操作系统内核 调用 CPU ,然后CPU调用DMA实现硬件IO,这样CPU就可以从操作系统内核切换到其他的应用这样就出现了 5种IO模型 blocking select poll epoll !!!
非阻塞IO code
非阻塞IO就实现了多个线程阻塞IO,因为不是阻塞态,所以进程还有CPU使用权,那么多客户端连接时,用户进程就会为下一个客户端的socket发送recvfrom请求,同时检测多个连接的数据时候准备就绪。
虽然已经不阻塞了,并且可以监听多个客户端连接。但是不推荐使用这种方法,因为需要循环调用recv(),它将大幅度提高CPU的占用率。
多路复用IO (multiplexting IO)
多路复用IO包括了select/poll/epoll。它的好处在于单个进程就可以同时处理多个网络连接的IO。当用户进程调用select,整个进程会被阻塞,但是和阻塞IO不同的是,kernel会监听所有的select负责的socket,当任何一个socket中数据被准备完毕,那么cpu会调度到用户进程中。他的原理就是select/epoll这个function会不断的轮训所有socket。IO复用同非阻塞IO本质一样,不过利用了新的select系统调用,由内核来负责本来是请求进程该做的轮询操作。
在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。
异步IO(Asynchronous IO)
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
以为不是block状态,所以应用程序是继续执行的,通过回调执行获取数据之后的业务逻辑。可以参照swoole的协程
5种IO的对比
可以看出前4种IO模型在数据从内核态copy到用户态的时候,用户进程是阻塞的,而异步IO全程都是非阻塞的
参考
https://blog.csdn.net/qiu18610714529/article/details/119512760