持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
一、服务与进程
进程是满足用户需求的一系列正在执行的任务,有的为了提供一个交互的界面;有的为了提供一个后台的演算;有的为了提供一个网络服务;有的为了利用磁盘资源做存储等。归根结底,进程就是需求的承载体。PC本身就是通用化的设备,自然所有的PC系统都要满足各种各样的需求。因此,提供一个直观的进程模型就是各种服务实现的基础。 进程在种类上对用户来说千奇百怪,有的服务于医疗;有的服务于电影娱乐;有的服务于游戏。这些进程在用户看来是按行业区分的,但是在操作系统看来,所有这些进程都是对操作系统所管理的硬件资源的请求。有的需要显示器;有的需要磁盘;有的需要CPU;有的需要网络。所以,在用户看来进程是一个个不同应用种类的服务,但是在操作系统看来,是侧重于不同种类资源请求的进程。
二、资源与进程
进程对资源的需求大体分为磁盘IO密集型、网络IO密集型、内存密集型、CPU密集型和显卡密集型。这5种类型也是PC所能提供的5个主要的资源。PC资源和世界资源一样,永远是有限的,关键是找到合理分配资源的方式。世界由不同的国家构成,世界资源的分配是一个博弈的过程。而PC可以只有一个主控系统,所以可以更加合理地充分地利用资源。但是目前实现的调度算法远远称不上优秀,在使用系统时几乎不能被感知到,除非你使用的进程耗尽了几乎100%的CPU时,才会把精力放在对调度系统的优化上。 PC有个特点,就是无论是哪种服务都需要有CPU的参与,而对其他资源的依赖很多都是非独占的,例如几个进程一起使用内存、网络和显卡等。除非你有多个CPU,否则你永远无法让一个CPU同时为多个进程服务。即使你有多个CPU,其数量也不会比进程多。所以CPU的时分复用就很重要,其他资源由于其并行性,其资源管理器提供的都是申请服务模型的服务,而只有CPU的资源管理器主动发起调度。所以,必须要理解我们常说的进程调度都是指调度CPU,因为只有CPU才最需要调度。但是随着各个服务的复杂化,对于IO或者显卡的请求也越来越呈现无法充分并行的情况,也就是资源不足,而CPU也越来越具有并行能力,即CPU资源逐渐充足。所以未来的调度进程也不一定只会以CPU的时间片作为调度单元。真理是:永远以瓶颈资源作为调度的核心。可能有那么一天,CPU不是问题(现在有24核的服务器,一般不会在使用CPU时遇到瓶颈),最大的瓶颈在网络,在于各个进程都同时对有限的网络带宽提出需求。这时CPU可能就会被作为服务的提供方,网络带宽的调度程序就变成了进程调度的核心话题了。请求满足模型与调度系统是同一个功能的两种不同视角的实现。
三、进程概念
进程这个概念最早是不存在的,用现在的话来说进程就是单任务的操作系统。但在以前人们认为一个在板子上跑起来的软件只有一条执行流水线是很正常的事情,但是随着业务逻辑越来越复杂,人们对一个板子同时做多个事情的要求也越来越大,于是人们想办法同时模拟出多个正在执行的代码段。之所以说模拟,是因为以前CPU着实只有单核,其同时执行多个代码就超出了物理限制。因此,只能切分CPU的时间片造成假并行的现象。即使是现在有了物理的多核,CPU的时间片也会被切分用以创造超过CPU个数的并行现象。C++的Boost库中的协程并不需要有多个并行的调度实体,调度是在库中模拟完成的。 对多任务需求的解决方案带来了有利和不利的结果。有利的是满足了这个编程需求,这个需求非常大,大部分新的实际应用如果没有这个需求根本不能实现,甚至没有这个需求的推动就没有现代的计算机技术。但同时带来的副作用是,无论需求多么大,只能想办法克服,毕竟需求是技术生存的原动力。还有两个最大的副作用是资源竞争与执行实体的调度。 要在一个只能运行一个代码流水线的CPU模拟运行多个代码流水线,通过设计上层概念,然后分时分配CPU资源,所以CPU被人们称为了资源。设计的上层概念有很多:进程、线程、workqueue、tasklet、softirq,这还仅仅是在Linux中目前仍在使用的代码流水线的概念。在当前的Linux内核中,进程与线程几乎是调度一致的,workqueue、tasklet、softirq等和进程一起参与调度算法的调度,因为不调度就意味着不能被CPU执行。这些代码流的概念服务于不同的用途,例如softirq和tasklet一般用于中断;workqueue一般用于驱动。而它们被调度的方法通常是一个专用的内核线程在后台执行。进程和线程的概念则一般用于用户空间。而在实际的实现上,softirq、tasklet或workqueue都可以被封装到某个线程,如此调度算法在调度的时候只需要认识线程一种结构就好了,精简算法逻辑是有好处的。但是这也仅仅是实现的方式。完全可以让调度算法认识各种不同的代码流概念,从而在调度时区别对待。 我们现在写用户空间的代码时只认识进程和线程,因为用户空间是内核的接口产品,用户空间的程序员对整个世界的认识。例如用户空间看到进程和线程时,大部分程序员都能说出进程与线程的区别。但是在内核空间,线程与进程没有太大的区别(需要理解进程组的概念),在用户空间看到的它们之间的区别其实是被伪造出来的。 既然进程的概念最后“胜出”了,也就是说为了说明现代的进程,必须要说明实现进程所必须付出的代价:进程调度、资源竞争、进程通信、进程关系以及进程概念是如何被制造出来的。