关于I/O——内存与CPU与磁盘之间的关系

关于I/O
一句话概括什么是I/O:简单的数据Copy 。内存与外部设备之间的copy就是I/O(input/)
I/O与CPU
通常CPU速度:纳秒级别(指令执行速度)
通常I/O速度:毫秒级别(磁盘的一次seek)
速度差异上千倍,需要解决如何匹配I/O和CPU速度、调度CPU等 。
假设你是一个急性子(CPU),需要等待一个重要的文件,不巧的是这个文件只能快递过来(I/O),那么这时你是选择什么事情都不干了,深情的的注视着门口就像盼望着你的哈尼一样专心等待这个快递呢?还是暂时先不要管快递了,玩个游戏看个电影刷会儿短视频等快递来了再说呢?很显然,更好的方法就是先去干其它事情,快递来了再说 。
因此这里的关键点就是手头上的事情可以先暂停,切换到其它任务,等快递过来了再切换回来 。
执行I/O时底层都发生了什么 1.关于Block:
上图内存中有两个进程,进程A和进程B,当前进程A正在运行,进程A中有一段读取文件的代码,不管在什么语言中通常都会自定义一个用来装数据的,然后调用read之类的函数,像这样:
read(buff);
这就是一种典型的I/O操作,当CPU执行到这段代码的时候会向磁盘发送读取请求,注意与CPU执行指令的速度相比,I/O操作操作是非常慢的,因此操作系统是不可能把宝贵的计算资料浪费在无谓的等待上的,这时由于外部设备执行I/O操作是相当慢的,因此在I/O操作完成之前进程是无法继续向前推进的,这就是所谓的阻塞,即通常所说的block 。操作系统检测到进程向I/O设备发起请求后就暂停进程的运行,需要记录下当前进程的运行状态并把CPU的PC寄存器指向其它进程的指令 。
2.关于I/O的阻塞队列和就绪队列:
进程有暂停就会有继续执行,因此操作系统必须保存被暂停的进程以备后续继续执行,显然我们可以用队列来保存被暂停执行的进程,如图所示,进程A被暂停执行并被放到阻塞队列中(注意,不同的操作系统会有不同的实现,可能每个I/O设备都有一个对应的阻塞队列,但这种实现细节上的差异不影响我们的讨论) 。
磁盘相当于在异步读取,而次数将读取结束回调放入I/O队列,CPU又去干其他事情了 。
实际上操作系统中除了有阻塞队列之外也有就绪队列,所谓就绪队列是指队列里的进程准备就绪可以被CPU执行了,你可能会问为什么不直接执行非要有个就绪队列呢?答案很简单,那就是僧多粥少,在只有2个核的机器上可以创建出成千上万个进程,CPU不可能同时执行这么多的进程,因此必然存在这样的进程,即使其一切准备就绪也不能被分配到计算资源,这样的进程就被放到了就绪队列 。现在进程B就位于就绪队列,万事俱备只欠CPU,如图所示:
注意观察上图,此时进程B在被CPU执行,磁盘在向进程A的内存空间中copy数据,数据copy和指令执行在同时进行,在操作系统的调度下CPU、磁盘都得到了充分的利用,这就是程序员的智慧所在 。
3.中断
此后磁盘终于将全部数据都copy到了进程A的内存中,这时磁盘通知操作系统任务完成,这就是中断 。
操作系统接收到磁盘中断后发现数据copy完毕,进程A重新获得继续运行的资格,这时操作系统小心翼翼的把进程A从阻塞队列放到了就绪队列当中,如图所示:
注意,操作系统是不会直接运行进程A的,进程A必须被放到就绪队列中等待,这样对大家都公平 。此后进程B继续执行,进程A继续等待,进程B执行了一会儿后操作系统认为进程B执行的时间够长了,因此把进程B放到就绪队列,并把进程A取出继续执行 。注意操作系统把进程B放到的是就绪队列,因此进程B被暂停运行仅仅是因为时间片到了而不是因为发起I/O请求被阻塞,如图所示: