标题: Linux Kernel核心中文手册 4 [打印本页] 作者: 老装 时间: 2007-7-18 23:02 标题: Linux Kernel核心中文手册 4 Linux Kernel核心中文手册 来自:蓝森林自由软件
Chapter 4 Processes (进程)
本章描述进程是什么以及 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 )。可中断的等待进程可以被信号中断,而不可中断的等待进程直接等待硬件条件,不能被任何情况中断。
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 向量表中。只要进程所属的其中一个组对于一个文件有访问权限,则这个进程就又对于这个文件的适当的组权限。
所有的进程部分运行与用户态,部分运行于系统态。底层的硬件如何支持这些状态各不相同但是通常有一个安全机制从用户态转入系统态并转回来。用户态比系统态的权限低了很多。每一次进程执行一个系统调用,它都从用户态切换到系统态并继续执行。这时让核心执行这个进程。 Linux 中,进程不是互相争夺成为当前运行的进程,它们无法停止正在运行的其它进程然后执行自身。每一个进程在它必须等待一些系统事件的时候会放弃 CPU 。例如,一个进程可能不得不等待从一个文件中读取一个字符。这个等待发生在系统态的系统调用中。进程使用了库函数打开并读文件,库函数又执行系统调用从打开的文件中读入字节。这时,等候的进程会被挂起,另一个更加值得的进程将会被选择执行。进程经常调用系统调用,所以经常需要等待。即使进程执行到需要等待也有可能会用去不均衡的 CPU 事件,所以 Linux 使用抢先式的调度。用这种方案,每一个进程允许运行少量一段时间, 200 毫秒,当这个时间过去,选择另一个进程运行,原来的进程等待一段时间直到它又重新运行。这个时间段叫做时间片。
需要调度程序选择系统中所有可以运行的进程中最值得的进程。一个可以运行的进程是一个只等待 CPU 的进程。 Linux 使用合理而简单的基于优先级的调度算法在系统当前的进程中进行选择。当它选择了准备运行的新进程,它就保存当前进程的状态、和处理器相关的寄存器和其他需要保存的上下文信息到进程的 task_struct 数据结构中。然后恢复要运行的新的进程的状态(又和处理器相关),把系统的控制交给这个进程。为了公平地在系统中所有可以运行( runnable )的进程之间分配 CPU 时间,调度程序在每一个进程的 task_struct 结构中保存了信息:
参见 kernel/sched.c schedule()
policy 进程的调度策略。 Linux 有两种类型的进程:普通和实时。实时进程比所有其它进程的优先级高。如果有一个实时的进程准备运行,那么它总是先被运行。实时进程有两种策略:环或先进先出( round robin and first in first out )。在环的调度策略下,每一个实时进程依次运行,而在先进先出的策略下,每一个可以运行的进程按照它在调度队列中的顺序运行,这个顺序不会改变。
4.3.1 Scheduling in Multiprocessor Systems (多处理器系统中的调度)
在 Linux 世界中,多 CPU 系统比较少,但是已经做了大量的工作使 Linux 成为一个 SMP (对称多处理)的操作系统。这就是,可以在系统中的 CPU 之间平衡负载的能力。负载均衡没有比在调度程序中更重要的了。
在一个多处理器的系统中,希望的情况是:所有的处理器都繁忙地运行进程。每一个进程都独立地运行调度程序直到它的当前的进程用完时间片或者不得不等待系统资源。 SMP 系统中第一个需要注意的是系统中可能不止一个空闲( idle )进程。在一个单处理器的系统中,空闲进程是 task 向量表中的第一个任务,在一个 SMP 系统中,每一个 CPU 都有一个空闲的进程,而你可能有不止一个空闲 CPU 。另外,每一个 CPU 有一个当前进程,所以 SMP 系统必须记录每一个处理器的当前和空闲进程。
在一个 SMP 系统中,每一个进程的 task_struct 都包含进程当前运行的处理器编号( processor )和上次运行的处理器编号( last_processor )。为什么进程每一次被选择运行时不要在不同的 CPU 上运行是没什么道理的,但是 Linux 可以使用 processor_mask 把进程限制在一个或多个 CPU 上。如果位 N 置位,则该进程可以运行在处理器 N 上。当调度程序选择运行的进程的时候,它不会考虑 processor_mask 相应位没有设置的进程。调度程序也会利用上一次在当前处理器运行的进程,因为把进程转移到另一个处理器上经常会有性能上的开支。