我们前面提到过linux内核的几个子系统,他们分别是进程管理、虚拟文件系统(文件管理)、内存管理和网络接口管理(本来应该还有设备管理的,但是linux是以文件的方式管理硬件设备的,所以把它归为文件管理里边)。对于现代操作系统来说,进程是一个核心概念,它也是我们理解操作系统内核必须首先掌握的一个概念。所以,这节我们首先介绍进程的概念。
我们从我们所熟悉的操作系统的操作开始:我们可以在一台PC上打开好多个网页、我们可以在看视频的时候跟女朋友聊天。就是说,从表象上来看,我们确实在同一时间运行了很多个程序。但实际上操作系统内部是做不到这点的(对于单核处理器来说),他们在一个具体的时间点有且仅有一条程序在运行,但我们分明看到我们同时运行了多个程序。其实很好解决,有时分复用概念的同学将会很容易理解这个概念:最容易想到的就是,我们给每个运行起来的程序都分配固定的运行时间,这个时间可以很短,然后让所有运行起来的程序都运行这么长一段时间再换另一个程序,这就是时间片轮转算法的雏形,不过实际上比这复杂多了,这里首先给一个宏观上的概念,只为好理解。
要说进程这个概念,不得不提的一个概念就是程序。程序就是一个文件,比如我们的一个视频播放器,它由一个可执行文件和很多其它文件构成,这就是一个程序,它更多的被视为一个存储体。而所谓进程,即当程序跑起来了它就是一个进程,比如我们双击我们的视频播放器的可执行文件,它就可以开始播放视频了。实际上,在进程运行的过程中,它肯定是要访问CPU和内存的,进程运行的过程中还会加载一些跟本进程相关的一些文件,也就是说,进程运行过程中是不停的申请占用资源的,即进程的运行过程是一个动态的过程,相对而言,程序是一个静态的存储体。
《linux操作系统原理与应用(第二版)》中给出了一个进程的明确定义:所谓进程是由正文段(Text)、用户数据段(User Segment)、以及系统数据段(System Segment)共同组成的一个执行环境。
正文段:即存放被执行的机器指令。那这个段肯定是只读的,它应该可以被很多正在运行的进程共享,即多个实现同一操作过程的话就可以共享该段代码。
用户数据段:存放进程在执行时直接操作的所有数据,显然这里的程序是可能被改变的,因而它应该被每个进程独享,抑或每个进程要有他专用的数据。
系统数据段:该段有效的存储程序运行的环境。这是程序和进程最重要的区别所在。我们说程序是一个静态存储体,它事实上就包括正文段和自己的私有数据,而当程序运行起来之后就会在系统数据段存放该程序的控制信息,进程通过这些控制信息来控制进程的各种运行状态。linux为每个进程建立了一个tast_struct数据结构来容纳这些控制信息,这个结构体被叫做进程控制块(PCB)。
进程是一个动态的实体,它具有生命周期,系统中进程的生死随时发生。譬如我们运行一个播放器,从双击它开始,到关闭它完成,这中间它要经过各种不同的事,比如有别的进程获取到CPU的时候它得停下来等,它可能由于系统中进程数太多卡死等等。这些都应该被完美的控制,那么表征这些控制信息的资源都应该被放在进程控制块中,程序运行过程中通过动态的修改、检测这些信息来决定程序在运行过程中的各种状态。
为了对进程从产生到消亡这个动态变化的过程进行捕获和描述,就需要定义进程在运行过程中的各种状态,并制定相应的状态转换策略,以此来控制进程的运行。显然,这些信息是应该放在进程控制块中的。
不同的操作系统对进程管理方式可能会不同,但无论如何进程最基本的三种状态是必须要体现的:
1)、运行态:此时进程占有CPU,并在CPU上运行。
2)、就绪态:进程已经具备运行条件(可能要申请的资源已到位),但由于CPU忙(被其他进程占用)而暂时不能运行。
3)、阻塞态:进程由于要申请的资源没有到而等待资源的到位,此时就算CPU空进程也不能投入运行。
这是三种必须有的状态,我们可以尝试为进程的这三种状态制定一个状态转换策略,即我们怎么决定程序何时进入哪种状态。
1)、运行态->阻塞态:此时进程正处于运行的状态,在此过程中,它发现它需要一些资源,但是这些资源没来(比如我们需要从控制台输入一些数据进去,我们不输入则程序就不继续运行),于是我们修改进程控制块中的状态信息,让它进入阻塞态,而调度程序检测进程控制块中的信息,发现该进程是阻塞态,于是马上做相应的处理,调入一个其他进程来运行,而让这个进程自己去等待(实现的方法很多,你可以专门做一个等待队列什么的)。
2)、运行态->就绪态:此时进程正处于运行的状态,但系统认为该进程占用CPU的时间过长,它觉得让别的进程等太长时间是不对的,于是修改进程控制块,让它进入就绪态(由于它是可以继续运行的,不存在等待资源),让别的进程也占用一会儿CPU,然后再运行此程序。
3)、就绪态->运行态:这个好办,此时程序正处于就绪态,就等CPU空闲了然后投入运行。
4)、阻塞态->就绪态:这个也比较好解决,当前程序处于阻塞态,它发现它要用的资源已经到了,然后马上就进入就绪态,等待占用CPU。
这些状态转换策略的实现是依靠一个叫调度程序的程序实现的,我们以后会讲到调度程序的实现。