Linux 学习笔记(五)- 进程管理


无论是系统管理员还是普通用户,监视系统进程的运行情况,并适时终止一些失控的进程是每天的例行事务…就像人生一样,总有那么几个失控的瞬间,怎么办呢,扼杀掉它。🤓

快速上手:结束一个失控的程序

传说中有一个古老而有名的“恶作剧”程序,会在 Shell 中不停地创建目录和文件。如果不赶快终止,那么它将在系统中创建一棵很深的目录树:

  1. 在主目录中用文本编辑器创建一个名为 badpro 的文本文件,包含以下内容:
1
2
3
4
5
6
7
8
9
#! /bin/bash
while echo "I'm making files! !"
do
mkdir adir
cd adir
touch afile

sleep 2s
done

这是一个 Shell 脚本, 会不停的新建目录和文件。为了让这个恶作剧表现得尽可能“温和”,这里让它在每次建完目录和文件后休息2秒钟。

  1. 将这个文件加上可执行权限,并从后台执行。
1
2
$ sudo chmod +x badpro
$ ./badpro &

FYI:运行这个程序存在一些风险。千万不要漏了 sleep 2s 这一行,否则创建的目录树的深度会很快超出系统的允许范围。在这种情况下,我们可能必须要使用 rm -fr adir 来删除这些“垃圾”目录。为什么要从后台运行?原因只有一个,即迫使自己使用 kill 命令杀死这个进程。

  1. 现在程序已经跑起来了,可以看到终端在不停地输出 I’m making files! ! 。新开一个终端窗口,使用 ps 命令看一下 badpro 的进程信息,使用管道配合 grep 命令查找 PID,这里 PID 唯一表示一个进程。ps 命令的输出中第二个字段表示 PID,根据 grep 的搜索结果,可以判断出 10221 就是 badpro 的 PID。
1
2
3
$ ps aux | grep badpro
tommy 10221 0.0 0.0 113288 1408 pts/0 S 10:08 0:00 /bin/bash ./badpro
tommy 10764 0.0 0.0 112828 984 pts/1 R+ 10:10 0:00 grep --color=auto badpro
  1. 现在使用 kill 命令”杀死“比尔,哦不,是”杀死“这个进程。
1
$ kill 10221
  1. 回到运行 badpro 的终端,可以看到程序已经终止了,记得把 adir 删掉就好了
1
$ rm -r adir

什么是进程

看似简单的概念往往很难给出定义,一个比较“正规”的说法是:进程是操作系统的一种抽象概念,用来表示正在运行的程序。其实,读者可以简单地把进程理解为正在运行的程序。Linux 是一种多用户、多进程的操作系统。在 Linux 的内核中,维护着一张表。这张表记录了当前系统中运行的所有进程的各种信息。Linux 内核会自动完成对进程的控制和调度。当然,这是所有操作系统都必须拥有的基本功能。内核中一些重要的进程信息如下:

  • 进程的内存地址;
    进程当前的状态;
    进程正在使用的资源;
    进程的优先级(谦让度);
    进程的属主。

Linux 提供了让用户可以对进程进行监视和控制的工具。在这方面,Linux 对系统进程和用户进程一视同仁,使用户能够用一套工具控制这两种进程。

读到这里,小咪突然想起来上大学的时候学习操作系统,那是一本黑色封面的教材,名字就是《操作系统》,整本书没有一个图片,全™是文字,给我们上课的女老师是计算机系的副主任,这门课真的是所谓的”读书“,她全程都是在读,可想而知这门课是旷课率最高的…哈哈哈…好了,不闹了~所以我们可以简单的理解为一个进程就是一个程序。

进程的属性

PID: 进程的 ID 号

用户管理那一篇曾经学到过,系统为每个用户都分配了用于标识其身份的 ID 号(UID)。同样地,进程也有这样一个 ID 号,被称作 PID。用 ID 确定进程的方法是非常有好处的,对于计算机而言,认识数字永远比认识一串字符方便得多,Linux 没有必要去理解那些对人类非常“有意义”的进程名。Linux 不仅自己使用 PID 来确定进程,还要求用户在管理进程时也提供相应的 PID 号。几乎所有的进程管理工具都接受 PID 号,而不是进程名。这也是为什么在“快速上手”环节中必需要使用 ps 命令获得 PID 号的原因。

PPID: 父进程的 PID

在 Linux 中,所有的进程都必须由另一个进程创建一除了在系统引导时,由内核自主创建并安装的那几个进程。当一个进程被创建时,创建它的那个进程被称作父进程,而这个进程则相应地被称作子进程。子进程使用 PPID 指出谁是其“父亲”,很容易可以理解,PPID 就等于其父进程的 PID。

在刚才的叙述中,多次用到了“创建”这个词,这是出于表述和理解上的方便。事实上在 Linux 中,进程是不能被“凭空”创建的。也就是说,Linux 并没有提供一种系统调用让应用程序“创建”一个进程。应用程序只能通过克隆自已来产生新进程。因此,子进程应该是其父进程的克隆体。所以 Gra-Gra-Gra Father 是🤓?

UID 和 EUID:真实和有效的用户 ID

只有进程的创建者和 root 用户才有权利对该进程进行操作。于是,记录一个进程的创建者(也就是属主)就显得非常必要。进程的 UID 就是其创建者的用户 ID 号,用于标识进程的属主。Linux 还为进程保存了一个“有效用户 ID 号”,被称作 EUID。这个特殊的 UID 号用来确定进程对某些资源和文件的访问权限。在绝大部分情况下,进程的 UID 和 EUID 是一样的,除了著名的 setuid 程序。

什么是 setuid 程序?用户管理中的 passwd 命令,这个命令允许用户修改自己的登录口令。但是密码保存在 /etc/shadow 文件中,这个文件对普通用户是不可读的,那么用户怎么能够通过修改 shadow 文件来修改自己的口令呢?这就是 setuid 的妙处了,通过使 passwd 在执行阶段具有文件所有者(也就是 root)的权限,让用户临时有了修改 shadow 文件的能力(当然这种能力是受到限制的)。因此,passwd 就是一个典型的 setuid 程序,其 UID 是当前执行这个命令的用户 ID,而 EUID 则是 root 用户的 ID (也就是0)。

GID 和 EGID:真实和有效的组 ID

类似的,进程的 GID 就是其创建者所属组的 ID,EGID 可以通过 setgid 程序来设置。但好像日常没什么用~

谦让度和优先级

顾名思义,进程的优先级决定了其受到 CPU “优待”的程度。优先级高的进程能够更早地被处理,并获得更多的处理器时间。Linux 内核会综合考虑一个进程的各种因素来决定其优先级。这些因素包括进程已经消耗的 CPU 时间、进程已经等待的时间等。在绝大多数情况下,决定进程何时被处理是内核的事情,不需要用户插手。

用户可以通过设置进程的“谦让度”来影响内核的想法。“谦让度” 和“优先级”刚好是一对相反的概念,高“谦让度”意味着低“优先级”,反之亦然。需要注意的是,进程管理工具让用户设置的总是“谦让度”,而不是“优先级”。如果希望让一个进程更早地被处理,那么应该把它的谦让度设置得低一些,使其变得不那么“谦让”。