Processes (进程)

3/8/2008来源:C/C++教程人气:5855


  本章描述进程是什么以及linux如何创建、治理和删除系统中的进程。

 

进程执行操作系统中的任务。程序是存放在磁盘上的包括一系列机器代码指令和数据的可执行的映像,因此,是一个被动的实体。进程可以看作是一个执行中的计算机程序。它是动态的实体,在处理器执行机器代码指令时不断改变。处理程序的指令和数据,进程也包括程序计数器和其他CPU的寄存器以及包括临时数据(例如例程参数、返回地址和保存的变量)的堆栈。当前执行的程序,或者说进程,包括微处理器中所有的当前的活动。Linux是一个多进程的操作系统。进程是分离的任务,拥有各自的权利和责任。假如一个进程崩溃,它不应该让系统中的另一个进程崩溃。每一个独立的进程运行在自己的虚拟地址空间,除了通过安全的核心治理的机制之外无法影响其他的进程。

 

在一个进程的生命周期中它会使用许多系统资源。它会用系统的CPU执行它的指令,用系统的物理内存来存储它和它的数据。它会打开和使用文件系统中的文件,会直接或者间接使用系统的物理设备。Linux必须跟踪进程本身和它使用的系统资源以便治理公平地治理该进程和系统中的其他进程。假如一个进程独占了系统的大部分物理内存和CPU,对于其他进程就是不公平的。

 

系统中最宝贵的资源就是CPU。通常系统只有一个。Linux是一个多进程的操作系统。它的目标是让进程一直在系统的每一个CPU上运行,充分利用CPU。假如进程数多于CPU(多数是这样),其余的进程必须等到CPU被释放才能运行。多进程是一个简单的思想:一个进程一直运行,直到它必须等待,通常是等待一些系统资源,等拥有了资源,它才可以继续运行。在一个单进程的系统,比如DOS,CPU被简单地设为空闲,这样等待的时间就会被浪费。在一个多进程的系统中,同一时刻许多进程在内存中。当一个进程必须等待时操作系统将CPU从这个进程拿走,并将它交给另一个更需要的进程。是调度程序选择了

下一次最合适的进程。Linux使用了一系列的调度方案来保证公平。

 

Linux支持许多不同的可执行文件格式,ELF是其中之一,java是另一个。Linux必须透明地治理这些文件,因为进程使用系统的共享的库。

 

4.1 Linux PRocesses(Linux的进程)

 

Linux中,每一个进程用一个task_strUCt(在Linux中task和process互用)的数据结构来表示,用来治理系统中的进程。Task向量表是指向系统中每一个task_struct数据结构的指针的数组。这意味着系统中最大进程数受task向量表的限制,缺省是512。当新的进程创建的时候,从系统内存中分配一个新的task_struct,并增加到task向量表中。为了更轻易查找,用current指针指向当前运行的进程。

参见include/linux/sched.h

 

除了普通进程,Linux也支持实时进程。这些进程必须对于外界事件迅速反应(因此叫做“实时”),调度程序必须和普通用户进程区分对待。虽然task_struct数据结构十分巨大、复杂,但是它的域可以分为以下的功能:

 

State 进程执行时它根据情况改变状态(state)。Linux进程使用以下状态:(这里漏掉了SWAPPING,因为看来没用到)

Running 进程在运行(是系统的当前进程)或者预备运行(等待被安排到系统的一个CPU上)

Waiting 进程在等待一个事件或资源。Linux区分两种类型的等待进程:可中断和不可中断的(interruptible and uninterruptible)。可中断的等待进程可以被信号中断,而不可中断的等待进程直接等待硬件条件,不能被任何情况中断。

Stopped 进程停止了,通常是接收到了一个信号。正在调试的进程可以在停止状态。

Zombie 终止的进程,因为某种原因,在task 向量表重任旧有一个task_struct数据结构的条目。就想听起来一样,是一个死亡的进程。

 

Scheduling Information 调度者需要这个信息用于公平地决定系统中的进程哪一个更应该运行。

Identifiers 系统中的每一个进程都有一个进程标识符。进程标识符不是task向量表中的索引,而只是一个数字。每一个进程也都有用户和组(user and group)的标识符。用来控制进程对于系统中文件和设备的访问。


Inter-Process Communication Linux支持传统的UNIX-ipC机制,即信号,管道和信号灯(semaphores),也支持系统V的IPC机制,即共享内存、信号灯和消息队列。关于Linux支持的IPC机制在第5章中描述。

Links 在Linux系统中,没有一个进程是和其他进程完全无关的。系统中的每一个进程,除了初始的进程之外,都有一个父进程。新进程不是创建的,而是拷贝,或者说从前一个进程克隆的(cloned)。每一个进程的task_struct中都有指向它的父进程和兄弟进程(拥有相同的父进程的进程)以及它的子进程的的指针。在Linux系统中你可以用pstree命令看到正在运行的进程的家庭关系。

 

init(1)-+-crond(98)

-emacs(387)

-gpm(146)

-inetd(110)

-kerneld(18)

-kflushd(2)

-klogd(87)

-kswapd(3)

-login(160)---bash(192)---emacs(225)

-lpd(121)

-mingetty(161)

-mingetty(162)

-mingetty(163)

-mingetty(164)

-login(403)---bash(404)---pstree(594)

-sendmail(134)

-syslogd(78)

`-update(166)

 

另外系统中的所有的进程信息还存放在一个task_struct数据结构的双向链表中,根是init进程。这个表让Linux可以查到系统中的所有的进程。它需要这个表以提供对于ps或者kill等命令的支持。

Times and Timers 在一个进程的生命周期中,核心除了跟踪它使用的CPU时间还记录它的其他时间。每一个时间片(clock tick),核心更新jiffies中当前进程在系统和用户态所花的时间综合。Linux也支持进程指定的时间间隔的计数器。进程可以使用系统调用建立计时器,在计时器到期的时候发送信号给自己。这种计时器可以是一次性的,也可是周期性的。

File system 进程可以根据需要打开或者关闭文件,进程的task_struct结构存放了每一个打开的文件描述符的指针和指向两个VFS I节点(inode)的指针。每一个VFS I节点唯一描述一个文件系统中的一个文件或目录,也提供了对于底层文件系统的通用接口。Linux下如何支持文件系统在第9章中描述。第一个I节点是该进程的根(它的主目录),第二个是它的当前或者说pwd目录。Pwd取自Unix命令:印出工作目录。这两个VFS节点本身有计数字段,随着一个或多个进程引用它们而增长。这就是为什么你不能删除一个进程设为工作目录的目录。

Virtual memory 多数进程都有一些虚拟内存(核心线程和核心守护进程没有),Linux核心必须知道这些虚拟内存是如何映射到系统的物理内存中的。

Processor Specific Context 进程可以看作是系统当前状态的总和。只要进程运行,它就要使用处理器的寄存器、堆栈等等。当一个进程暂停的时候,这些进程的上下文、和CPU相关的上下文必须保存到进程的task_struct结构中。当调度者重新启动这个进程的时候,它的上下文就从这里恢复。

 

4.2 Identifiers (标识)

 

Linux,象所有的Unix,使用用户和组标识符来检查对于系统中的文件和映像的访问权限。Linux系统中所有的文件都有所有权和许可,这些许可描述了系统对于该文件或目录拥有什么样的权限。基本的权限是读、写和执行,并分配了3组用户:文件属主、属于特定组的进程和系统中的其他进程。每一组用户都可以拥有不同的权限,例如一个文件可以让它的属主读写,它的组读,而系统中的其他进程不能访问。

 

Linux使用组来给一组用户赋予对文件或者目录的权限,而不是对系统中的单个用户或者进程赋予权限。比如你可以为一个软件项目中的所有用户创建一个组,使得只有他们才能够读写项目的源代码。一个进程可以属于几个组(缺省是32个),这些组放在每一个进程的task_struct结构中的groups向量表中。只要进程所属的其中一个组对于一个文件有访问权限,则这个进程就又对于这个文件的适当的组权限。

一个进程的task_struct中有4对进程和组标识符。

Uid,gid 该进程运行中所使用的用户的标识符和组的标识符

Effective uid and gid 一些程序把执行进程的uid和gid 改变为它们自己的(在VFS I节点执行映像的属性中)。这些程序叫做setuid程序。这种方式有用,因为它可以限制对于服务的访问,非凡是那些用其他人的方式运行的,例如网络守护进程。有效的uid 和gid来自setuid程序,而uid和gid 仍然是原来的。核心检查特权的时候检查有效 uid和gid。

File system uid and gid 通常和有效uid和gid相等,检查对于文件系统的访问权限。用于通过NFS安装的文件系统。这时用户态的NFS服务器需要象一个非凡进程一样访问文件。只有文件系统uid和gid改变(而非有效uid和gid)。这避免了恶意用户向NFS的服务程序发送Kill信号。Kill用一个非凡的有效uid和gid发送给进程