3.8. 进程和守护进程

FreeBSD 是一个多任务操作系统。 这就意味着好像一次可以运行一个以上的程序。 每个占用一定时间运行的程序就叫 进程 (process)。 你运行的每一个命令会至少启动一个新进程,还有很多一直运行着的系统进程, 用以维持系统的正常运作。

每个进程用来标识的一个编号就叫 进程 ID, 或叫 PID。 而且,就像文件那样,每个进程也有所属用户和所属群体。 所属用户和所属群体使用在这方面:确定这个进程可以打开那些文件和那些设备, 从而在初期使用文件的权限。 多数的进程都有一个父进程, 而进程是依靠父进程来启动的。 例如,假如您把命令输入到shell里那shell是一个进程,而您运行的各个命令同样是进程, 那么,shell就是您各个运行进程的父进程。 而这方面有一个例外的进程就叫init(8)init始终是首个进程,,所以他的PID始终是1, 而init在FreeBSD起动时由内核自动启动。

有些程序在运行时,并不是为了让用户连续输入而设计的,它们在启动后将断开与终端的连接。例如,Web 服务器响应 Web 请求,而不是用户输入。另一个例子是邮件服务器。这些类型的程序被称为守护进程(daemons)。daemon这个词来自于希腊神话,代表着一个既非善也非恶的实体,它无形中执行着有用的任务。这也是 BSD 的吉祥物是一个穿着运动鞋,拿着干草叉的快乐的守护神的原因。

守护进程的程序命名通常在最后加一个 dBIND 是伯克利互联网域名服务 (而实际执行的程序名称则是 named), Apache web系统的程序就叫 httpd, 在行式打印机上的打印守护进程就是 lpd。 这只是一种惯例,不是标准或硬性规定。 例如,为Sendmail而应用的主要mail守护进程就叫sendmail, 却不叫maild,这和您推测的一样。

3.8.1. 查看进程

在系统上,有两个命令对进程观察非常有用:ps(1)top(1)。 这个ps命令作用是观察当前运行进程的状态, 显示他们的PID,使用了多少内存,它们启动的命令行。 而top命令则是显示所有运行进程,并在以秒计的短时内更新数据。 您能交互式的观察您计算机的工作。

默认情况下,ps(1) 仅显示出您自己所运行的命令。 例如:

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

在这个例子里您可看到,从 ps(1) 输出的每一列是有规律的。 PID 就是进程ID,这个较早前已讨论过了。 PID号的分配由 1一直上升直到99999, 当您运行到超过限制时,这些编号会回转分配 (仍在使用中的 PID 不会分配给其他进程)。 TT这一列显示了程序运行所在的终端, 目前可以安全地忽略。 STAT 显示程序的状态,也可以安全地被忽略。 TIME是程序在CPU处理时间──运行的时间量, 并不是指您程序启动到现在的所用的时间。 许多程序碰巧遇到某方面在他们之前要花费大量CPU处理时间时,他们就必须等候。 最后, COMMAND 是运行程序时使所用的命令行。

ps(1)支持使用各种选项去改变显示出来的内容, 最有用的一个就是auxwwa选项显示出所有运行进程的内容, 而不仅仅是您的进程。 u选项显示出进程所归属的用户名字以及内存使用, x 选项显示出后台进程。 而 ww 选项表示为 ps(1) 把每个进程的整个命令行全部显示完, 而不是由于命令行过长就把它从屏幕上截去。

top(1) 的输出类似:

% top
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

输出被分成两部分。头部(前五六行)显示了最后一个进程运行的PID、系统负载平均数(衡量系统的繁忙程度)、系统正常运行时间(上次重启后的时间)和当前时间。标题中的其他数字涉及到有多少个进程在运行,使用了多少内存和交换空间,以及系统在不同的CPU状态下花费了多少时间。如果ZFS文件系统模块已经加载,ARC行表示从内存缓存中读取了多少数据,而不是从磁盘中读取了多少数据。

标题下面的列,包含了类似ps(1)输出的信息,如PID、用户名、CPU时间量和启动进程的命令等。默认情况下,top(1)也会显示进程占用的内存空间量。这被分成两列:一列是总大小,一列是常驻大小。总大小是指应用程序需要多少内存,常驻大小是指现在实际使用的内存量。

top(1) 每两秒自动刷新一次,您可以用s改变刷新的秒数。

3.8.2. 关闭进程

要与执行中的程序或Daemon沟通唯一的方法是透过kill(1)指令传送信号(Signal)。信号有很多种,有些有特定的意义,有些则是会由应用程序来解读,应用程序的说明文件会告诉您该程序是如何解读信号。使用者只能送信号给自己所拥有的程序,送信号给其他人的程序会出现权限不足的错误。唯一的例外是root使用者,他可以送信号给任何人的程序。

操作系统在某些情况也会送信号给应用程序。假设有个应用程序写得不好,企图要存取它不该碰的內存的时候,FreeBSD会送一个Segmentation Violation信号(SIGSEGV)给这个程序。如果有一个应用程序用了alarm(3)的系统呼叫(System call)要求系统在过一段时间之后发出通知,时间到了的时候系统就会发出通知信号( SIGALRM)给该程序。

SIGTERMSIGKILL这两个信号可以拿来终止程序。用SIGTERM结束程序是比较有礼貌的方式,该程序收到信号后可以把自已所使用的日志档关闭及其他要在结束前要做的事完成,然后在关掉程序之前结束掉手边的工作。在某些情况下程序有可能会忽略SIGTERM,如它正在做一些不能中断的工作的话。

SIGKILL 不会被进程忽视。向进程发送 SIGKILL 通常能结束进程[1]

您可能会去使用 SIGHUPSIGUSR1SIGUSR2信号。 这都是些通用的信号,各种应用程序都可以应用 在各方面的信号发送。

假如您改变了web系统的配置文件──并想web系统去重读它的配置, 您可以停止然后再启动httpd。但这样做web系统会导致一个短暂 的中断周期,那样是不受欢迎的。几乎所有的守护进程在编写时,都会指定对SIGHUP 信号进行响应从而重读配置文件。 所以, 最好的方法, 就不是杀死并重启 httpd, 而是发一个 SIGHUP 信号给它。 因为在这方面没有一个标准,不同的守护进程有不同的用法,所以不了解时应读一下守护进程的文档。

过程 3.1. 向进程发送信号(Signal)

这个示例将会示范如何送一个信号给inetd(8)inetd(8)的配置文件是/etc/inetd.conf,而inetd(8)会在收到SIGHUP的时候重新读取这个配置文件。

  1. 使用pgrep(1)找到要接收信号的进程的 PID。本例中inetd(8) 的 PID 是 198:

    % pgrep -l inetd
    198  inetd -wW
  2. 使用 kill(1) 来发送信号。由于inetd(8) 属于 root。在发送信号前需获取 root 权限。

    % su
    Password:
    # /bin/kill -s HUP 198

    和大多数 UNIX® 命令一样, kill(1) 如果完成了任务, 就不会给出任何消息。 假如您发送信号给一个不属于您的进程, 您会看到 kill: PID: Operation not permitted. 假如输错了PID号,把信号发送到其他进程,那是坏事。 或者您侥幸,把信号发送到不存在的进程, 您会看见 kill: PID: No such process.

    为什么使用 /bin/kill?:

    许多shell提供了内建 kill 命令, 这样, shell就能直接发送信号,而不是运行 /bin/kill。 这点非常有用, 但不同shell有不同的语法来指定发送信号的名字, 与其试图把它们学完倒不如简单地直接使用 /bin/kill ...

发送其他的信号也很相似, 只要在命令行替换 TERMKILL 就行了。

重要:

在系统上随意杀死进程是个坏主意,特别是init(8), 它的进程ID是1,它非常特殊。可以运行 /bin/kill -s KILL 1 命令来让系统迅速关机。 当您按下 Return (回车)键之前, 一定要 详细检查您运行 kill(1) 时所指定的参数。



[1] 还是有少数东西不能被中断。例如有个程序正在从网络上的别的电脑读一个档案,而那部电脑因为某些理由连不到,那这个程序就是一个不能中断的程序。通常在经过2分钟左右之后这个程序会逾时。当发生逾时的时候这个程序就会被结束掉了。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.