目录
一、前言
二、What Is a Process
三、Process Structure
1、The Process Table
2、Viewing Processes
3、System Processes
4、Process Scheduling
一、前言
进程和信号是 Linux 操作环境的基本组成部分。它们控制 Linux 和所有其他类 unix 计算机系统执行的几乎所有活动。了解 Linux 和 UNIX 如何管理进程对任何系统程序员、应用程序程序员或系统管理员都有好处。
在本中,我们将学习如何在 Linux 环境中处理进程,以及如何找出计算机在任何给定时间正在做什么。我们还将了解如何从我们自己的程序中启动和停止其他进程,如何使进程发送和接收消息,以及如何避免僵尸进程。特别是,我们将了解:
(1)进程的结构、类型和调度;
(2)以不同的方式启动新进程;
(3)父进程、子进程和僵尸进程;
(4)什么是信号以及如何使用它们。
二、What Is a Process
UNIX 标准,特别是 IEEE Std 1003.1, 2004 版,将进程定义为:
“一个地址空间,在该地址空间中执行一个或多个线程,以及这些线程所需的系统资源。”
我们将在后面讨论线程。现在,我们将进程仅仅看作一个正在运行的程序。像 Linux 这样的多任务操作系统允许多个程序同时运行。正在运行的程序的每个实例都构成一个流程。这一点在像 X 窗口系统 (通常简称为 X) 这样的窗口系统中表现得尤为明显。与 Windows 一样,X 提供了一个图形用户界面,允许多个应用程序同时运行。每个应用程序都可以显示一个或多个窗口。作为一个多用户系统,Linux 允许多个用户同时访问系统。每个用户可以同时运行多个程序,甚至同一程序的多个实例。系统本身运行其他程序来管理系统资源和控制用户访问。
正在运行的程序或进程由程序代码、数据、变量(占用系统内存)、打开的文件(文件描述符)和环境组成。通常,Linux 系统将在进程之间共享代码和系统库,以便在任何时候内存中只有一份代码副本。
三、Process Structure
让我们看看如何在操作系统中安排几个进程。如果两个用户 (neil 和 rick) 同时运行 grep 程序,在不同的文件中查找不同的字符串,所使用的进程可能如下图所示。
如果你可以像下面代码中那样快速运行 ps 命令,并且在搜索完成之前,输出可能包含如下内容:
每个进程分配一个唯一的编号,称为进程标识符或 PID。这通常是 2 到 32,768 之间的正整数。当一个进程启动时,按顺序选择下一个未使用的数字,并从 2 重新启动这些数字,以便它们绕到一起。数字 1 通常为特殊的 init 进程保留,它管理其他进程。
迟点我们再回来讨论 init。在这里,我们可以看到 neil 和 rick 启动的两个进程被分配了标识符 101 和 102。将由 grep 命令执行的程序代码存储在磁盘文件中。通常,Linux 进程不能写入用于保存程序代码的内存区域,因此代码以只读的形式加载到内存中。
从上图中可以看到,尽管这个区域(code)不能写入,但可以安全地共享,还可以共享系统库。因此,即使许多正在运行的程序调用 printf,在内存中也只需要有一个 printf 副本。这是一种更复杂但与Windows 中的 动态链接库(dll) 工作方式类似的方案。
正如在上面的图中所看到的,一个额外的好处是包含可执行程序 grep 的磁盘文件更小,因为它不包含共享库代码。对于单个程序来说,这似乎没有多少节省,但是提取标准 C 库的公共例程可以在整个操作系统中节省大量的空间。
当然,并不是程序运行所需的所有东西都可以共享。例如,它使用的变量对于每个进程都是不同的。在本例中,我们将看到传递给 grep 命令的搜索字符串作为变量 s 出现在每个进程的数据空间中。它们是独立的,通常不能被其他进程读取。
两个 grep 命令中使用的文件也不同,进程有自己的一组用于文件访问的文件描述符。
此外,进程有自己的堆栈空间,用于函数中的局部变量和控制函数调用和返回。它也有自己的环境空间,包含可以单独为该进程建立的环境变量。
进程还必须维护自己的程序计数器,记录它在执行过程中到达的位置,也就是执行线程。
在 POSIX Threads 中,我们将看到当使用线程时,进程可以有多个执行线程。在许多 Linux 系统和一些 UNIX 系统中,在名为 /proc 的目录中有一组特殊的“文件”。它们的特殊之处在于,它们允许我们在进程运行时“查看”进程内部,就像它们是目录中的文件一样,在“文件处理板块”已经简要介绍了 /proc 文件系统。
最后,因为 Linux 和 UNIX 一样,有一个虚拟内存系统,可以将代码和数据分页到硬盘的某个区域,因此可以管理的进程比物理内存要多很多。
1、The Process Table
Linux 进程表就像一个数据结构,描述了当前加载的所有进程,例如它们的 PID、状态和命令字符串,这是 ps 输出的一类信息。
操作系统使用它们的 PID 来管理进程,它们被用作进程表的索引。表的大小是有限的,因此系统支持的进程数量是有限的。早期的 UNIX 系统被限制为 256 个进程。更现代的实现大大放宽了这一限制,可能只受可用来构造进程表项的内存的限制。
2、Viewing Processes
ps 命令显示正在运行的进程、另一个用户正在运行的进程或系统上的所有进程。以下是更多的示例输出:
$ ps -ef
它显示了关于许多进程的信息,包括 Linux 系统上 X 下的 Emacs编辑器所涉及的进程。例如,TTY 列显示进程是从哪个终端启动的,TIME 显示到目前为止所使用的CPU时间,CMD 列显示用于启动进程的命令。让我们仔细看看其中的一些。
neil 655 428 0 18:24 tty4 00:00:00 –bash
初始登录是在虚拟控制台4上执行的。这只是这台机器的控制台。正在运行的 shell 程序是 Linux 默认的bash。
root 467 433 0 18:12 tty1 00:00:00 sh /usr/X11R6/bin/startx
X 窗口系统是由命令 startx 启动的。这是一个 shell 脚本,用于启动 X 服务器并运行一些初始的 X 程序。
root 717 716 13 18:28 pts/0 00:00:01 emacs
该进程表示运行 Emacs 的 X 中的一个窗口。它是由窗口管理器在响应新窗口请求时启动的。已经为 shell 分配了一个新的伪终端 pts/0,用于读取和写入。
root 512 1 0 18:12 tty1 00:00:01 gnome-help-browser --sm-client-i
这是窗口管理器启动的 GNOME 帮助浏览器。
默认情况下,ps 程序只显示与终端、控制台、串行线或伪终端保持连接的进程。
其他进程不需要在终端上与用户通信就可以运行。这些通常是 Linux 用来管理共享资源的系统进程。我们可以使用 ps ,使用 -e 选项查看所有这样的进程,并使用 -f 获取“完整”信息。
ps 命令的确切语法和输出格式可能因系统而略有不同。Linux 中使用的 ps 的 GNU 版本支持以前几个 ps 实现中的选项,包括 UNIX 的 BSD 和 AT&T 变体中的选项,并添加了更多自己的选项。有关 ps 的可用选项和输出格式的详细信息,可以参阅 man 手册。
3、System Processes
下面是在另一个 Linux 系统上运行的一些进程。为了清晰起见,已对输出进行了缩写。在以下示例中,我们将看到如何查看进程的状态。ps 的 STAT 输出提供了指示当前状态的代码。常用代码如下表所示。其中一些的含义在后面会变得更清楚。
$ ps ax
然后你可以看到一个非常重要的进程。
通常,每个进程由另一个称为其父进程的进程启动。这样启动的进程称为子进程。当 Linux 启动时,它运行一个程序,即主父进程和进程号1 init。如果我们愿意,这是操作系统进程管理器和所有进程的始祖。我们很快将遇到的其他系统进程是由init启动的,或者由init启动的其他进程启动的。登录过程就是这样一个例子。init 为每个可用于登录的串行终端或拨号调制解调器启动一次 getty 程序。这些在 ps 输出中是这样显示的:
getty 进程等待终端上的活动,用熟悉的登录提示符提示用户,然后将控制权传递给登录程序,该程序设置用户环境,最后启动 shell。当用户 shell 退出时,init 启动另一个 getty 进程。我们可以看到,启动新进程并等待它们完成的能力是系统的基础。在后面,我们将看到如何在我们自己的程序中使用系统调用 fork、exec 和 wait 来执行相同的任务。
4、Process Scheduling
另一个 ps 输出的例子是 ps 命令本身的条目:
这表明进程 21475 处于运行状态 (R),并且正在执行命令 ps ax。因此,这个过程在它自己的输出中被描述状态。指示器只显示程序已经准备好运行,而不一定显示它实际上正在运行。在单处理器计算机上,一次只能运行一个进程,而其他进程则依次等待。这些转换称为时间段,非常短,给人一种程序同时运行的感觉。R+只是表明程序是一个前台任务,不等待其他进程完成,也不等待输入或输出完成。这就是为什么我们可能会在 ps 输出中看到两个这样的进程。(另一个标记为正在运行的常见进程是 X 显示服务器。)
Linux 内核使用进程调度程序来决定哪个进程将接收下一个时间片。它使用进程优先级来实现这一点。具有高优先级的进程可以更频繁地运行,而其他的进程,如低优先级的后台任务,则运行得不那么频繁。在 Linux 中,进程不能超过它们所分配的时间片。它们是先发制人的多任务,因此它们在没有它们的合作的情况下被暂停和恢复。旧系统,如 Windows 3,通常要求流程显式地让步,以便其他流程可以恢复。
在像 Linux 这样的多任务系统中,多个程序可能在竞争同一资源,执行短时间工作爆发和暂停输入的程序被认为比那些通过不断计算某些值或不断查询系统以查看是否有新的输入来占用处理器的程序表现得更好。行为良好的程序被称为好程序,从某种意义上说,这种“好”是可以衡量的。操作系统根据“nice”值(默认值为0)和程序的行为确定进程的优先级。长时间运行而不暂停的程序通常优先级较低。例如,在等待输入时暂停的程序会得到奖励。这有助于保持与用户交互的程序的响应性;当它在等待来自用户的输入时,系统会增加它的优先级,以便当它准备好恢复时,它具有高优先级。我们可以使用 nice 设置过程 nice 值,并使用 renice 调整它。nice 命令将进程的 nice 值增加 10,使其优先级降低。我们可以使用 -1 或 -f (用于长输出)选项来查看活动进程的漂亮值。我们感兴趣的值显示在 NI(nice) 列中。
$ ps -l
在这里,我们可以看到正在运行的 oclock 程序(作为进程1362)具有一个默认的漂亮值。如果用命令启动它:
$ nice oclock &
它会被分配一个 +10 的 nice 值。如果使用命令调整此值:
$ renice 10 1362
1362: old priority 0, new priority 10
时钟程序运行的频率将降低。我们可以再次看到 ps 修改后的 nice 值。
status 列现在还包含 N,表示 nice 值已从默认值更改:
ps 输出的 PPID 字段表示父进程 ID,导致该进程启动的进程的 PID,如果该进程不再运行,则是init (PID 1)。Linux 调度器根据优先级决定允许哪个进程运行。当然,具体的实现各不相同,但优先级更高的流程运行得更频繁。在某些情况下,如果高优先级的进程已经准备好运行,那么低优先级的进程根本不运行。
以上,进程与信号(一)
祝好