Linux操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

6.1 程序和进程的概念

操作系统的重要任务之一是使用户充分、有效地利用有限的系统资源,也就是在系统资源一定或有限的情况下,要同时执行更多程序,高效率地完成更多的任务。进程、作业和任务调度是操作系统的重要任务之一。本章的主要内容是从系统的外部来观察系统中与进程(process)、作业(job)或任务(task)相关的行为,并实施某些控制让系统工作得更好,或按照用户的意图来完成指定工作。

6.1.1 程序、进程、作业和任务

程序是一个存储在存储介质上的文件,它是数据文件,但又不同于一般数据文件,不仅具有可执行的属性,而且具有执行的内容。它体现了编程人员要求计算机完成所要求功能时所应该采取的顺序步骤,是一个在先后顺序上按严格次序进行的操作序列。

进程是一个程序的动态执行过程。程序的执行过程表现为对某个或某些数据的加工和处理过程,它要完成某种特定的功能。进程突出了一个程序动态的执行过程,而一个程序存储在介质上的文件或数据,则是一种静态信息。

在多任务、多用户系统中,进程具有并发和并行特征。在一个系统内部,同时可以运行很多进程,甚至可以让同一个程序多次同时运行,但它们对应不同的进程。在系统内有不同的进程标志,也就是说,不同的进程可以运行同一程序,但可能是该程序所对应的数据集有所不同。

作业(job)或任务(task)是用户需要计算机完成某项任务时要求计算机所做工作的集合,一个作业可能需要几个程序联合完成。当用户向计算机提交了一个在指定时间执行或重复执行的工作,就可以认为是向计算机提交了作业或任务。

6.1.2 三类进程

UNIX/Linux系统是多用户、多任务操作系统,在系统内同时运行着很多进程,如系统管理进程、服务器进程和用户进程等。这些进程大致可以分为三类。

1.前台进程

前台进程(Foreground Process)是指用户直接控制的用于完成某个任务的进程,因此也叫终端交互式进程。它从标准输入读数据,向标准输出写数据,将错误信息输出到标准错误。也可以是用户直接交互控制的完成某种功能的程序,如文字加工与处理、游戏、浏览器和不同行业的各种应用程序等。

2.后台进程

后台进程(Background Process)是指在系统后台运行的进程。在一个系统可能运行着很多一般用户不知道、也不关心的进程,如系统本身用于管理和控制的进程、各种服务器进程等。前台进程也可放在后台运行,但这时要用到I/O重定向。

守护进程(Daemon Process)也叫服务器或精灵进程,它是后台进程的一种,该类进程永久不停地运行着,以等待其他进程提出服务请求,从而为它们提供服务。

3.批处理进程

批处理进程(Batch Process)是用户按照某种意图将一批作业和任务通过编程的方法提交给系统,让系统在某个合适的时间来调度和执行的进程。批处理进程是通过shell编程等方法,将所要处理的工作规划好,之后由系统启动并依次执行的进程。严格地说,该类进程不是独立的进程,它们是在某个shell程序的控制下解释执行的。

6.1.3 Linux操作系统的启动

Linux系统的启动是通过加电和系统自检后,把主引导程序(自举程序)装入内存并把控制权交给它实现的。在主引导程序的控制下装入Linux的引导程序(Linux的引导块或逻辑引导扇区),并把控制权交给它,然后在该引导程序的控制下系统继续引导。在把核心装入内存后,系统开始进一步的初始化过程。首先初始化系统内部数据结构,如构造空闲缓冲区、初始化区表结构、页表项等,然后将根文件系统安装到根“/”下,并创建系统的0#进程、设置它的运行环境。至此,系统的内核已经启动完成。但是,还不能做太多的工作,至少还缺少与用户的交互部分。为此,系统继续引导,创建1#进程,然后由1#进程做进一步的初始化工作。

1#进程继续初始化过程,按照/etc/inittab文件的内容和规定启动服务、管理进程、为每个终端生成一个子进程,然后等待用户在终端上注册,至此启动过程完毕。整个启动过程如图6-1所示。

图6-1 Linux系统的启动过程

6.1.4 0#进程与1#进程

0#进程与1#进程是UNIX/Linux系统中两个最重要的进程。

在UNIX系统中,0#进程是唯一只在核心态下执行的进程。它的功能有三:调度分配处理机;负责进程交换;初始化时创建1#进程。在Linux系统中,0#进程在创建出1#进程后,变成了空闲进程(Idler),当系统中没有其他进程就绪时,它才运行。

1#(init)进程是系统启动时创建的创建进程的进程。它的主要作用是根据/etc/inittab的内容初始化系统、创建系统运行所需的进程。在Fedora Core 9中,还要根据运行级别触发运行级别初始化事件,执行/etc/event.d/内的rcN(0≤N≤6)脚本程序进行初始化。系统初始化完成后,1#进程变成回收进程,专门领养没有父进程的孤儿进程或回收状态为ZOMBIE的僵尸进程。

从进程创建关系来看,0#进程创建了1#进程,它是1#进程的父进程。1#进程在系统启动过程中创建了系统所需要的其他进程,这些被创建的进程是1#进程init的子进程,而子进程又可创建属于自己的子进程。因此,系统中除了0#进程外,1#进程是其他所有进程的祖先进程。

Linux系统进程之间的父子关系可用pstree命令来查看,图6-2为某Linux系统的进程树的局部。

图6-2 某Linux系统的进程树的局部

6.1.5 进程状态及转换

由于在操作系统中,一个CPU上同时只能运行一个进程,但在多用户、多任务环境下,从宏观上来讲,同时运行着很多进程,因此在这些正在运行着的进程中,在任一时刻只能有一个进程占有处理机而真正运行。也就是说,众多运行着的进程中,它们宏观上是并行的,但微观上是串行的。因此,就存在有的进程在运行,有的进程在等待问题。事实上,进程的状态远不止执行和等待两个,还有用户态执行、核心态执行、等待、睡眠、就绪等状态。

UNIX系统的进程状态及转换如图6-3所示。在UNIX系统中,正在执行的进程可能处于下列9种状态之一:

图6-3 UNIX系统的进程状态及转换

(1)用户态执行。进程在用户态执行时处于此状态。

(2)系统态执行。当用户进程需要系统提供服务时,转入内核执行,进入此状态。

(3)内存就绪。进程仍未运行或已经运行了一段时间,由于时间片用完而放弃CPU,进入此状态。也可由其他状态转换而来,如I/O得到满足或由外存就绪状态换入。处于此状态的进程已具备运行条件,只要调度程序调度它占有处理机,就可立即运行。

(4)内存中睡眠。进程因等待资源而进入此状态。在当正在执行的进程需要资源但暂时还不能满足时进入此状态。这些资源包括各种外设,如打印机、磁盘和处理机等。

(5)外存中睡眠。进程在内存中睡眠过程中,因内存不足而导致它被内存换出到交换区或交换设备上,进入此状态。

(6)外存就绪。处于外存睡眠或在外存等待的进程等待的事件发生或条件满足后,进入此状态。处于外存睡眠的进程,必须首先变为外存就绪,然后才能变为内存就绪。只有内存就绪的进程才能占有处理机,从而得到执行的机会。

(7)从系统态返回。当进程从系统服务返回时处于此状态。在此状态下将重新计算各进程的优先级,若此时系统中有了比当前进程优先级高的进程,则当前进程的执行权将被剥夺,而让优先级高的进程执行。被剥夺执行权的进程被放在就绪队列中排队,等待调度。

(8)创建状态。进程刚被创建时处于此状态。一般来说刚被创建的进程将会被优先执行,所以刚创建的进程是处于就绪状态的。

(9)僵尸状态。这是进程在整个生存期内的最后一个状态。当进程完成指定任务或因某些特殊原因结束时,它将进入这种状态。此时它不再活跃,所占的大部分资源已经释放,但仍然在进程控制表中保留下一个记录来记录它的退出码和计时等信息供父进程使用。只要它的父进程对它进行善后处理,就马上彻底结束。

图6-4为Linux进程状态及转换图。在Linux系统中,进程刚被创建或因申请的资源到位等原因进入就绪队列等待调度时,处于就绪状态(图6-4 中的①)。当调度程序调度其执行时,它将占有CPU。在执行过程中可能遇到以下问题:

图6-4 Linux系统的进程状态及转换

(1)睡眠等待资源。当资源未被满足时进入此状态。等待资源有两情况:浅度睡眠(TASK_INTERRUPTIBLE)(图6-4中的②)和深度睡眠(TASK_UNINTERRUPTIBLE)(图6-4中的④)。前者表示进程在等待队列中睡眠,但在此状态下的进程可以被信号唤醒;后者表示进程在等待队列中睡眠,但不能被信号或中断唤醒,直到它所等待的资源到位。

(2)停止状态(TASK_STOPPED)(图6-4中的③)。处于停止状态的进程还可以再次恢复运行。导致进程停止有两个原因:收到了停止信号;被其他进程跟踪。当它收到继续信号后,就可加入就绪队列了。

(3)僵尸状态(TASK_ZOMBIE)(图6-4中的⑤):执行完毕或被信号终止的进程进入此状态,等待它的父进程对它进行善后处理,之后退出系统。

图6-4并不能真正反映Linux系统进程状态之间的转换,细节问题还要参考图6-3。