同步 vs 异步
区分同步还是异步的关键在于:当内核将数据准备好之后,是否会将数据从内核空间拷贝到用户空间,会则是异步,不会则是同步
异步IO:
用户线程发起IO操作后,可以立即去做其他事情。
当内核将数据准备好之后,会自动将数据从内核空间拷贝到用户空间,拷贝完成之后,用户线程过来直接取即可,一刻也不用等。
同步IO:
当内核准备好数据之后,不会自动将数据从内核空间拷贝到用户空间,仍需要用户线程发起请求才会将数据从内核空间拷贝到用户空间,拷贝过程需要等待。
同步IO下面细分阻塞和非阻塞:
- 阻塞IO是用户线程从发起IO请求开始就一直阻塞等待
- 非阻塞IO虽然在用户发起请求时会立即返回,但是仍需要用户线程自己将数据从内核空间拷贝到用户空间
同步IO
阻塞IO
recvfrom解释:用于从已连接套接字
上接收数据,并捕获数据发送源的地址。
这种方案有什么问题?
首先当并发较大的时候,需要创建大量的线程来处理连接,需要占用大量的系统资源。
连接建立完成以后,如果当前线程没有数据可读,将会阻塞在read操作上造成线程资源的浪费
阻塞IO模式下内存读取磁盘数据过程
看下阻塞IO模式下,内存读取磁盘数据的全过程,可以重复理解阻塞IO原理,后面的四种模式都是在阻塞IO基础上的优化
现在内存中有两个进程,进程A和进程B,当前进程A正在运行,如图所示:
现在进程A要读取磁盘中的文件
在I/O操作完成之前进程是无法继续向前推进的,外部设备执行I/O操作又是相当慢的,所以进程需要等待IO完成,这就是阻塞
。操作系统检测到进程向I/O设备发起请求后就暂停进程的运行,
怎么暂停运行呢?
很简单,只需要记录下当前进程的运行状态并把CPU的PC寄存器
指向其它进程的指令就可以了。
进程有暂停就会有继续执行,因此操作系统必须保存被暂停的进程以备后续继续执行,显然我们可以用队列
来保存被暂停执行的进程,如图所示,进程A被暂停执行并被放到阻塞队列
中
这时操作系统已经向磁盘发送了I/O请求
,因此磁盘driver开始将磁盘中的数据copy到进程A的buff中,虽然这时进程A已经被暂停执行了,但这并不妨碍磁盘向内存中copy数据。注意,现代磁盘向内存copy数据时无需借助CPU的帮助
,这就是所谓的DMA(Direct Memory Access)
,这个过程如图所示:
让磁盘先copy着数据,我们接着聊
现在进程B就位于就绪队列,万事俱备只欠CPU,如图所示:
为什么不直接执行非要有个就绪队列呢?
答案很简单,那就是僧多粥少,在即使只有1个核的机器上也可以创建出成千上万个进程,CPU不可能同时执行这么多的进程,因此必然存在这样的进程,即使其一切准备就绪也不能被分配到计算资源,这样的进程就被放到了就绪队列。
此时操作系统将进程B从就绪队列
中取出,找出进程B被暂停时执行到的机器指令的位置
,然后将CPU的PC寄存器
指向该位置,这样进程B就开始运行。
此后磁盘终于将全部数据都copy到了进程A的内存中,这时磁盘通过中断
通知操作系统任务完成了,进程A重新获得继续运行的资格,这时操作系统把进程A从阻塞队列
放到了就绪队列
当中。
此后进程B继续执行,进程A继续等待,进程B时间片用完
之后被放到就绪队列
,把进程A取出并继续执行。
以上就完成了一个简单的内存读取磁盘数据的阻塞式IO。
非阻塞IO
和阻塞IO相比,内核会立即返回,返回后获得足够的CPU时间继续做其他的事情。
相比阻塞IO节省了大量时间,但是还不够好。
整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。
IO多路复用
异步IO
异步I/O
在等待数据和接收数据
都是非阻塞的,可以处理其他的逻辑,用户进程将整个IO操作交由内核完成,
系统会负责把数据从内核空间拷贝到用户空间,无需线程自己再进行阻塞的读写,内核完成后会发送通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。