假象:“并行”运行多个进程
现代操作系统都支持多任务同时执行。在这里,操作系统对我们用户“营造了一种假象”,让CPU看上去是用不完的,能够不断地添加新的程序使它们同时运行。
但每核CPU在某一时刻都只能执行一个进程。使用操作系统的人是不会去关注CPU是单核还是多核的,每个人都希望操作系统能同时运行多个程序,比如可以同时看网页、发邮件、聊QQ、听音乐等。
所以操作系统提供的多程序同时运行是“伪并行”。要想实现真正的并行运行多个进程,只能使用多核CPU来实现。
OS营造这种假象的技术是“CPU时间共享(time sharing)”,也称为“分时系统”,即CPU执行一会进程A,然后停下来去执行一会进程B,再停下来执行一会进程A。即,每个进程都只执行一会,然后CPU切换去执行其它进程,每一段执行和切换的时间很短(每秒能执行很多个进程),使得用户以为进程根本未停过,从而形成了假象。
例如,下图描述了分时系统的进程切换方式。进程A执行一会后切换到进程B,B执行一会后又切换到C,C执行一会后又切换到B,再切换回C、A、C、D。
这种不断切换进程的机制,其关键技术是CPU从一个进程切换到另一个进程,这是通过“上下文切换(context switch)”实现的。上下文切换可以直接理解为进程的切换,之所以使用“上下文”这个术语,是因为CPU要从进程A切换到进程B时,必然要保存进程A的执行环境,比如进入切换时进程A执行到了哪个地方,以便下次切换回进程A的时候能够从这里恢复继续执行,此外,既然要切换到进程B,必然还要恢复之前已保存好的进程B的执行环境,才能知道从哪里开始继续执行进程B。而在计算机世界中,“环境”和“上下文”通常是可以互换的概念,所以CPU的上下文切换正是两个前后进程执行环境的切换。
上面每个进程执行一会就会切换到另一个进程,这个执行的时长称为进间片。当一个进程耗尽所有时间片后,将切换到其他进程,那么时间片长好还短好?进间片分配过短,上下文切换就会越频繁,而且上下文切换是有开销的,时间片过长,则每个进程需要等待运行时间就越长,意味着交互性越差。一个常见的时间片长度是100 ms,但是现在LINUX(从LINUX2.6.23开始)的时间片是动态计算的,并非完全固定的值。
CPU时间共享的好处是提升了交互性,因为每个进程都可以尽快地执行起来,都能够更快地被响应,使得每个进程都像是被公平对待,这是非常感人的。但是时间共享却带来了另外一个问题,站在进程的角度上看,每个进程从启动开始到执行完成,中间需要花费更长时间,而且很多时间处于等待状态。
再考虑另外一个问题,既然要不断的切换进程,操作系统如何选择下一个要执行的进程?例如下图中,当进程D开始进程切换时,下一个要执行的是哪个进程呢?
选择下一个要执行的进程,是通过操作系统的进程调度程序(也称为调度器或调度类)来决定的,调度器根据一些调度算法策略决定哪个进程将幸运地被选中(即,被调度到)作为下一个要执行的进程。
而进程调度算法策略的两个关键性指标正是响应时间和周转时间。响应时间体现的是交互性和公平性,响应时间越短,表示交互时能更快得到响应,比如当敲下键盘时,用户肯定不希望还要等一段时间才显示字符。周转时间体现的是何时完成任务的问题,周转时间越短,表示从启动开始到执行结束所花的时间更短,也就是更快完成任务。
另外,虽然在分时系统下,通过为每个进程分配固定的时间片能保证每个进程都有机会执行,但总会有一些进程是比较重要的,它们的优先级比较高,它们可以从当前正在执行的进程处抢占CPU,这称为“抢占式多任务处理”。当然,这里的抢占并不是真的无条件抢到CPU,CPU给谁是由操作系统的调度策略决定的,“抢占”只是表明某进程比较重要,应当优待,比如让该进程尽快执行或让它多执行一会。