> For the complete documentation index, see [llms.txt](https://handbook.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://handbook.bsdcn.org/di-3-zhang-freebsd-ji-chu/3.8.-jin-cheng-he-shou-hu-jin-cheng.md).

# 3.8.进程和守护进程

FreeBSD 是一个多任务操作系统。每个在任意时刻运行的程序都称为一个 *进程（process）*。每个正在运行的命令都会启动至少一个新进程，并且 FreeBSD 还会运行一些系统进程。

每个进程都有一个唯一的编号，称为 *进程 ID（PID）*。类似于文件，每个进程也有一个所有者和所属组，系统会依据所有者和组的权限来判断进程可以访问哪些文件和设备。大多数进程也有一个启动它们的父进程。例如，shell 是一个进程，而在 shell 中启动的任何命令也都是进程，它们的父进程就是该 shell。唯一的例外是特殊进程 [init(8)](https://man.freebsd.org/cgi/man.cgi?query=init\&sektion=8\&format=html)，它总是在系统引导时第一个启动，进程 ID 永远是 `1`。

某些程序并不是设计来持续接受用户输入的，它们会在合适的时机脱离终端。例如，Web 服务器是用来响应网络请求而不是用户输入的。邮件服务器也是这种程序的一个例子。这类程序被称为 *守护进程（daemon）*。daemon 一词源自希腊神话，指的是那种既非善也非恶、却在幕后默默执行有用任务的存在。这正是 BSD 吉祥物——一只穿着运动鞋、手持三叉戟的可爱小恶魔——的由来。

按照惯例，守护进程程序通常以字母 'd' 结尾命名。例如，BIND（Berkeley Internet Name Domain）的实际运行程序是 `named`；Apache Web 服务器程序是 `httpd`；打印服务的后台处理进程是 `lpd`。这只是命名习惯，例如 Sendmail 的主邮件守护进程叫做 `sendmail`，而不是 `maild`。

## 3.8.1. 查看进程

可以使用 [ps(1)](https://man.freebsd.org/cgi/man.cgi?query=ps\&sektion=1\&format=html) 或 [top(1)](https://man.freebsd.org/cgi/man.cgi?query=top\&sektion=1\&format=html) 查看系统中正在运行的进程。要显示当前运行的进程、它们的 PID、所使用的内存，以及启动它们的命令，可以使用 [ps(1)](https://man.freebsd.org/cgi/man.cgi?query=ps\&sektion=1\&format=html)。要以交互方式每隔几秒更新显示所有进程，以便实时观察系统正在执行的任务，可以使用 [top(1)](https://man.freebsd.org/cgi/man.cgi?query=top\&sektion=1\&format=html)。

默认情况下，[ps(1)](https://man.freebsd.org/cgi/man.cgi?query=ps\&sektion=1\&format=html) 只显示当前用户拥有的正在运行的命令。例如：

```sh
% ps
```

输出可能类似如下：

```sh
 PID TT  STAT    TIME COMMAND
8203  0  Ss   0:00.59 /bin/csh
8895  0  R+   0:00.00 ps
```

[ps(1)](https://man.freebsd.org/cgi/man.cgi?query=ps\&sektion=1\&format=html) 的输出包含多个列。`PID` 列表示进程的 ID。PID 从 1 开始分配，一直到 99999，然后回绕到起点，但若某个 PID 已在使用，则不会重新分配。`TT` 列显示程序运行的终端设备，`STAT` 显示程序的状态，`TIME` 表示进程在 CPU 上实际运行的时间（而不是启动以来的总时间，因为大多数程序大部分时间都在等待）。最后，`COMMAND` 是启动程序时使用的命令。

可以通过不同的选项改变显示的信息。其中一个最有用的组合是 `auxww`，其中：

* `a` 显示所有用户的进程，
* `u` 显示用户名和内存使用信息，
* `x` 显示守护进程，
* `ww` 则让 ps 显示完整命令行，而不会因过长而截断。

[top(1)](https://man.freebsd.org/cgi/man.cgi?query=top\&sektion=1\&format=html) 的输出类似如下：

```sh
% top
```

输出示例如下：

```sh
last pid:  9609;  load averages:  0.56,  0.45,  0.36              up 0+00:20:03  10:21:46
107 processes: 2 running, 104 sleeping, 1 zombie
CPU:  6.2% user,  0.1% nice,  8.2% system,  0.4% interrupt, 85.1% idle
Mem: 541M Active, 450M Inact, 1333M Wired, 4064K Cache, 1498M Free
ARC: 992M Total, 377M MFU, 589M MRU, 250K Anon, 5280K Header, 21M Other
Swap: 2048M Total, 2048M Free

  PID USERNAME    THR PRI NICE   SIZE    RES STATE   C   TIME   WCPU COMMAND
  557 root          1 -21  r31   136M 42296K select  0   2:20  9.96% Xorg
 8198 dru           2  52    0   449M 82736K select  3   0:08  5.96% kdeinit4
 8311 dru          27  30    0  1150M   187M uwait   1   1:37  0.98% firefox
  431 root          1  20    0 14268K  1728K select  0   0:06  0.98% moused
 9551 dru           1  21    0 16600K  2660K CPU3    3   0:01  0.98% top
 2357 dru           4  37    0   718M   141M select  0   0:21  0.00% kdeinit4
 8705 dru           4  35    0   480M    98M select  2   0:20  0.00% kdeinit4
 8076 dru           6  20    0   552M   113M uwait   0   0:12  0.00% soffice.bin
 2623 root          1  30   10 12088K  1636K select  3   0:09  0.00% powerd
 2338 dru           1  20    0   440M 84532K select  1   0:06  0.00% kwin
 1427 dru           5  22    0   605M 86412K select  1   0:05  0.00% kdeinit4
```

输出分为两个部分：前五六行是标题区，显示了最近运行的进程 ID、系统负载（衡量系统繁忙程度）、系统运行时间（自上次引导以来），以及当前时间。其余数据包括：运行中进程数量、已用内存和交换空间（swap），以及 CPU 各状态所占百分比。如果加载了 ZFS 文件系统模块，还会显示一行 `ARC`，指示有多少数据是从内存缓存而非磁盘中读取的。

标题下面是与 ps 输出类似的各列信息，包括 PID、用户名、CPU 占用时间和启动进程的命令。top 默认还显示进程占用的内存空间，这分为两个列：一列是进程总共申请的内存（SIZE），另一列是当前实际使用的内存（RES）。

[top(1)](https://man.freebsd.org/cgi/man.cgi?query=top\&sektion=1\&format=html) 会每两秒自动刷新一次显示内容，可通过 `-s` 参数指定不同的刷新间隔。

## 3.8.2. 终止进程

与任何正在运行的进程或守护进程通信的一种方式是使用 [kill(1)](https://man.freebsd.org/cgi/man.cgi?query=kill\&sektion=1\&format=html) 发送一个 *信号（signal）*。有多种不同的信号；有些具有特定含义，其他的则可在应用程序文档中找到解释。用户只能向自己拥有的进程发送信号，若尝试向他人的进程发送信号将会导致权限拒绝错误。唯一的例外是 `root` 用户，它可以向任何人的进程发送信号。

操作系统本身也可以向进程发送信号。如果某个应用程序设计不良，试图访问其不应访问的内存，FreeBSD 会向该进程发送“段错误（Segmentation Violation）”信号（`SIGSEGV`）。如果某个应用程序使用了 [alarm(3)](https://man.freebsd.org/cgi/man.cgi?query=alarm\&sektion=3\&format=html) 系统调用来在一段时间后收到通知，它会收到“闹钟”信号（`SIGALRM`）。

可以用两种信号来终止一个进程：`SIGTERM` 和 `SIGKILL`。`SIGTERM` 是一种较为温和的终止方式，因为进程可以接收到信号、关闭任何已打开的日志文件，并尝试完成当前任务后再关闭。在某些情况下，如果进程正处于无法被打断的任务中，它可能会忽略 `SIGTERM`。

`SIGKILL` 是进程无法忽略的信号。向进程发送 `SIGKILL` 通常会立即终止该进程 ①。

其它常见的信号包括 `SIGHUP`、`SIGUSR1` 和 `SIGUSR2`。由于这些是通用信号，不同的应用程序会有不同的响应方式。

例如，在修改 web 服务器的配置文件之后，需要让 web 服务器重新读取配置。如果重新启动 `httpd`，可能会造成 web 服务的短暂中断。更好的方式是向该守护进程发送 `SIGHUP` 信号。需注意，不同守护进程的行为可能不同，因此请查阅对应文档以确定 `SIGHUP` 是否能达到预期效果。

> **重要**
>
> 随意终止系统中的某个进程是个坏主意。特别是 [init(8)](https://man.freebsd.org/cgi/man.cgi?query=init\&sektion=8\&format=html)，即 PID 1，是一个特殊进程。运行 `/bin/kill -s KILL 1` 会迅速但非正常地关闭系统。*务必*在按下 **Return** *之前*，再次确认传递给 [kill(1)](https://man.freebsd.org/cgi/man.cgi?query=kill\&sektion=1\&format=html) 的参数。

* ① 有一些任务是无法中断的。例如，如果进程试图从网络上另一台计算机的文件中读取数据，而另一台计算机不可用，则该进程被认为是不可中断的。最终，进程会超时，通常在两分钟后。若发生超时，进程将被终止。
