IBM的LPI复习资料之LPI101-Topic103 :GNU和Unix命令(5)创建、检测、结束进程

摘要:学习Linux的进程管理,包括:如何前后前后台进程,找出运行的程序,杀死进程,让进程在你离开一天以后还保持运行。

1 概述

本文帮助你学习Linux进程管理的基本知识:

  • (1) 管理前台和后台工作
  • (2) 如何在你登出系统之后自动执行一个进程
  • (3) 监视进程
  • (4) 选择和排序进程
  • (5) 向进程发送信号

1.1 预备知识

需要有一个能够工作的Linux系统,在上面实验脚本,本文的截图是从Ubuntu上实验获取的。

2 管理前台和后台工作

如果你停下来思考一下,很容易发现除了终端程序之外,还有很多其他的程序在运行。实际上,如果你使用图形桌面环境的话,你可能已经打开了多个终端仿真窗口、浏览器程序、游戏、电子表格程序等等。以前的例子中,我们都是先输入命令,然后等待命令执行完成,等待的过程中不能做其他的事情。在本文中,你将会在终端窗口中同时做更多的事情。

当你在终端窗口运行一个命令的时候,你是在前台运行它的。很多命令运行的很快,然而假设你想在一个图形桌面上显示数字时钟的情况。当然了,现在很多的图形环境头已经提供了这么一个功能,我们忽略这个事实,现在只是为了做实验。

如果你已经安装了X Window系统,也就很可能已经附带安装了xclock或者xeyes程序。如果没有安装这些程序,就按装名字类似xorg-x11-apps或者x11-apps的软件包。我们在例子中使用xclock。可以使用如下命令:

xclock -d -update 1

其中的-update 1控制每秒更新一次,默认情况下是每分钟更新一次。运行结果如下图所示。不幸的是,此时终端窗口失去了焦点,没有了提示符。所以,非常需要夺回控制权。庆幸的是,bash有一个挂起键(Ctl+z),按下这对组合键,终端窗口将会重新出现提示符,如下所示。

$ xclock -d -update 1
^Z
[1]+ stopped     xclock -d -update 1
ian@attic4:~$ fg %1
xclock -d -update 1
^Z
[1]+  Stopped                 xclock -d -update 1
ian@attic4:~$ fg %?clo
xclock -d -update 1
^Z
[1]+  Stopped                 xclock -d -update 1
ian@attic4:~$ bg
[1]+ xclock -d -update 1 &

时钟还显示在桌面上,只是它已经停止了运行,这就是挂起操作。实际上,如果你拖动其他的窗口经过时钟的一部分,这部分都不会重新绘制。注意到终端输出的消息,其中的1是job号。你可以通过运行fg %1来重新启动这个时钟程序。你也可以使用全部或者部分程序的名字,如fg %xclock或者fg %?clo。如果你没有给fg提供任何参数,那么它将会重新启动最后挂起的那个进程,在本例中,就是xclok。重启进程后,fg也把这个job带回了前台,你也就重新失去了bash的提示符。你需要做的是把job放入后台,这就是bg的作用,它使用和fg一样的参数。bg和fg一样,也是恢复xclock的运行,不同的是bg没有把xclock带入前台,而是放入后台执行,这样终端窗口依旧有提示符,同时时钟还在正常运行。

2.1 使用 "&"

当你把xclock放入后台运行后,输出的消息并没有Stopped,而是在末尾加一个&。实际上,把一进程放入后台执行,并不需要先把它挂起。只要在运行它的命令行后加上&即可。现在让我们后台运行一个模拟时钟。

ian@attic4:~$ xclock -bg wheat -update 1&
[2] 4320

注意现在的输出包含了一个job编号以及进程ID。关于PID我们稍后再说。现在我们使用jobs命令来看看有什么job在运行。使用-l选项来列出PID,正如你看到的,job 2确实PID为4320。注意[2]+中的+号,它表示2号job是当前job,当使用fg不带参数运行时,这个当前job就会被送到前台。

ian@attic4:~$ jobs -l
[1]-  3878 Running                 xclock -d -update 1 &
[2]+  4320 Running                 xclock -bg wheat -update 1 &
在我们解决与后台job有关的问题之前,先来构建一个穷人的数字时钟。我们使用sleep命令来造就一个2秒的延迟,使用date命令来打印当前的时间。我们使用while构造的循环来包围这两个命令。然后在后台运行这个组合体。
ian@attic4:~$ (while sleep 2; do date;done)&
[2] 4856
ian@attic4:~$ Tue Jan 19 09:23:30 EST 2010
Tue Jan 19 09:23:32 EST 2010
Tue Jan 19 09:23:34 EST 2010
fTue Jan 19 09:23:36 EST 2010
Tue Jan 19 09:23:38 EST 2010
gTue Jan 19 09:23:40 EST 2010

( while sleep 2; do
    date;
done )
Tue Jan 19 09:23:42 EST 2010
Tue Jan 19 09:23:44 EST 2010
Tue Jan 19 09:23:46 EST 2010
^C
这个Job的PID是4856。每隔2秒钟,date命令输出当前时间。当我们输入fg命令后,这个时钟就进入了前台,它依然没两秒输出一次,不过由于在前台运行,我们再无法在bash输入命令,只能通过Ctrl+c来停止这个程序了。

2.2 标准IO和后台进程

前面例子中,date命令的输出与fg命令的输入混在了一起。这导致了一个有趣的问题。如果一个后台进程需要从标准输入获取数据,将会发生什么?(译者注:因为bash也从stdin获取数据)。

我们用来启动后台进程的终端进程成为控制终端。除非重定向,否则后台进程的stdout和stderr都定向到控制终端。同样,后台进程也期望从控制终端获得输入。但是控制终端无法把你键入的字符重定向到后台进程的stdin。这种情况下,bash就会挂起进程,这样也就不再运行。你可以会把它放入前台然后提供给它一些输入数据。

ian@attic4:~$ (date; cat - > bginput.txt;date)&
[2] 5070
ian@attic4:~$ Tue Jan 19 10:33:13 EST 2010


[2]+  Stopped                 ( date; cat - > bginput.txt; date )
ian@attic4:~$ 
ian@attic4:~$ fg
( date; cat - > bginput.txt; date )
some text
more text
Tue Jan 19 10:33:31 EST 2010
ian@attic4:~$ cat bginput.txt 
some text
more text

3 退出登陆后运行一个进程

实践中,你可能想把后台进程的标准IO流重新定向到文件。即便这样还存在另外一个问题:如果控制终端关闭了,或者用户登出系统了,将会发生什么?答案是不同的shell结果不同。如果shell发送一个SIGHUP信号,那么后台的程序可能会关闭。现在我们考虑一个解决这个问题的方法。

3.1 nohup

nohup命令用来启动一个程序,从而导致这个程序会忽略掉hangup信号,并且把stdout和stderr重定向到一个文件。这个文件默认是nohup.out或者$HOME/nohup.out。如果这个文件不可写,那么程序就不会运行。如果你想输出到其他地方,就重定向stdout,stderr。

nohup不能执行一个管线或者命令列表。解决方式是把管线和列表写入一个文件,然后用bash来执行它。如下所示。

ian@attic4:~$ echo "while sleep 30; do date;done">pmc.sh 
ian@attic4:~$ nohup sh pmc.sh&
[2] 5485
ian@attic4:~$ nohup: ignoring input and appending output to `nohup.out'

ian@attic4:~$ nohup bash pmc.sh&
[3] 5487
ian@attic4:~$ nohup: ignoring input and appending output to `nohup.out'

老版本的nohup不会把状态信息写到控制终端上,所以可能不会看到错误的状态。

ian@attic4:~$ nohup . pmc.sh >mynohup.out 2>&1 &
[4] 5853
ian@attic4:~$ 
[4]+  Exit 126                nohup . pmc.sh > mynohup.out 2>&1

4 监视进程状态

前面我們使用了jobs命令來查看job的ID。

4.1 ps

另外有一个命令ps用来显示进程状态信息。请记住,ps是process status的缩写。ps可以接受0或者多个PID作为参数,从而显示这些进程的状态信息。如果我们使用jobs命令的-p选项,输出就只剩下了进程ID。我们可以使用这个结果作为ps的参数。

ian@attic4:~$ jobs -p
3878
5485
5487
ian@attic4:~$ ps $(jobs -p)
  PID TTY      STAT   TIME COMMAND
 3878 pts/1    S      0:06 xclock -d -update 1
 5485 pts/1    S      0:00 sh pmc.sh
 5487 pts/1    S      0:00 bash pmc.sh

如果没有给ps提供选项,只有控制终端是当前终端的进程才会被显示出来。许多其他的选项,比如-f(ull), -j(obs), -l(ong)用来控制输出的信息量。如果你没有提供参数,那么--forest选项将会显示出进程的树形关系,如下。

ian@attic4:~$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
ian       2643  2093  0 Jan18 pts/1    00:00:00 bash
ian       3878  2643  0 09:17 pts/1    00:00:06 xclock -d -update 1
ian       5485  2643  0 15:00 pts/1    00:00:00 sh pmc.sh
ian       5487  2643  0 15:01 pts/1    00:00:00 bash pmc.sh
ian       6635  5485  0 15:41 pts/1    00:00:00 sleep 30
ian       6645  5487  0 15:42 pts/1    00:00:00 sleep 30
ian       6647  2643  0 15:42 pts/1    00:00:00 ps -f
ian@attic4:~$ ps -j --forest
  PID  PGID   SID TTY          TIME CMD
 2643  2643  2643 pts/1    00:00:00 bash
 3878  3878  2643 pts/1    00:00:06  \_ xclock
 5485  5485  2643 pts/1    00:00:00  \_ sh
 6657  5485  2643 pts/1    00:00:00  |   \_ sleep
 5487  5487  2643 pts/1    00:00:00  \_ bash
 6651  5487  2643 pts/1    00:00:00  |   \_ sleep
 6658  6658  2643 pts/1    00:00:00  \_ ps

4.2 free

free用来查看系统的内存使用情况。默认的大小单位是KB,选项-b表示字节,-k表示KB,-m表示MB,-g表示GB。-t显示一个汇总行,-s则指定刷新显示的间隔,单位为秒,可以是小数。(译者注:-/+这行表示把缓存作用的内存空间排除后)

[root@centos192 work]# free
             total       used       free     shared    buffers     cached
Mem:       3924872    3216928     707944          0     521896    1744736
-/+ buffers/cache:     950296    2974576
Swap:      7574520          0    7574520
[root@centos192 work]# free -mt
             total       used       free     shared    buffers     cached
Mem:          3832       3141        691          0        509       1703
-/+ buffers/cache:        928       2904
Swap:         7396          0       7396
Total:       11229       3141       8088

4.3 uptime

uptime命令在一行中显示出如下信息:当前系统时间、自开机系统运行时间、登陆用户数、最近1,5,15分钟内的CPU平均负载。

[root@centos192 work]# uptime
 16:16:51 up 99 days,  7:06,  1 user,  load average: 0.00, 0.00, 0.00

5 选择和排序要显示的进程

5.1 使用ps

前面我们使用ps命令只列出了控制终端是当前终端的进程,注意前面例子中的SID列。-a选项用来显示所有拥有控制终端的进程,-x显示所有没有控制终端的进程,-e则显示所有进程。

ian@attic4:~$ ps -af # 显示所有拥有控制终端的进程的全部信息
UID        PID  PPID  C STIME TTY          TIME CMD
ian       3878  2643  0 09:17 pts/1    00:00:06 xclock -d -update 1
ian       5485  2643  0 15:00 pts/1    00:00:00 sh pmc.sh
ian       5487  2643  0 15:01 pts/1    00:00:00 bash pmc.sh
ian       7192  5485  0 16:00 pts/1    00:00:00 sleep 30
ian       7201  5487  0 16:00 pts/1    00:00:00 sleep 30
ian       7202  2095  0 16:00 pts/0    00:00:00 ps -af

注意,TTY这一列表示的就是进程的控制终端。ps还有很多其他的选项,很多是控制显示哪些信息以及如何显示。也有一些选项用来选择特定的进程,如-u选择特定用户的进程,-C选择特定命令的进程。

#用来显示命令getty的进程,并显示用户,PID,控制终端,和命令行状态列
ian@attic4:~$ ps -C getty -o user,pid,tty,time,comm
USER       PID TT           TIME COMMAND
root      1192 tty4     00:00:00 getty
root      1196 tty5     00:00:00 getty
root      1209 tty2     00:00:00 getty
root      1219 tty3     00:00:00 getty
root      1229 tty6     00:00:00 getty
root      1731 tty1     00:00:00 getty
有时候,你需要对数据结果进行某种排序。这可以通过--sort选项来指定。默认是按照升序排列。
ian@attic4:~$ ps -aj --sort -sid,+comm	# 按照sid降序,comm升序排列所有拥有控制终端的进程
  PID  PGID   SID TTY          TIME CMD
 5487  5487  2643 pts/1    00:00:00 bash
 9434  9434  2643 pts/1    00:00:00 ps
 5485  5485  2643 pts/1    00:00:00 sh
 9430  5485  2643 pts/1    00:00:00 sleep
 9433  5487  2643 pts/1    00:00:00 sleep
 3878  3878  2643 pts/1    00:00:10 xclock
 8019  8019  2095 pts/0    00:00:00 man
 8033  8019  2095 pts/0    00:00:00 pager
ian@attic4:~$ ps -aj --sort sid,comm # 按照sid升序,comm升序排列
  PID  PGID   SID TTY          TIME CMD
 8019  8019  2095 pts/0    00:00:00 man
 8033  8019  2095 pts/0    00:00:00 pager
 5487  5487  2643 pts/1    00:00:00 bash
 9435  9435  2643 pts/1    00:00:00 ps
 5485  5485  2643 pts/1    00:00:00 sh
 9430  5485  2643 pts/1    00:00:00 sleep
 9433  5487  2643 pts/1    00:00:00 sleep
 3878  3878  2643 pts/1    00:00:10 xclock
更多的ps选项,参考man手册页以及ps --help。

5.2 使用top

如果需要动态的显示进程信息,要使用top命令。它用来实时显示最新的进程信息,而且还提供了有用的汇总。使用q命令可以退出。

top - 16:07:22 up 18:29,  5 users,  load average: 0.03, 0.02, 0.00
Tasks: 170 total,   1 running, 169 sleeping,   0 stopped,   0 zombie
Cpu(s):  2.1%us,  0.5%sy,  0.0%ni, 97.4%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4057976k total,  1543616k used,  2514360k free,   194648k buffers
Swap: 10241428k total,        0k used, 10241428k free,   613000k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 6820 ian       20   0  506m  78m  26m S    1  2.0   0:23.97 firefox
 1381 root      20   0  634m  40m  18m S    1  1.0   2:06.74 Xorg
 2093 ian       20   0  212m  15m  10m S    1  0.4   0:13.53 gnome-terminal
 6925 ian       20   0 1118m 298m  19m S    1  7.5   1:07.04 java
 6855 ian       20   0 73416  11m 8808 S    1  0.3   0:05.01 npviewer.bin
 7351 ian       20   0 19132 1364  980 R    0  0.0   0:00.07 top
    1 root      20   0 19584 1888 1196 S    0  0.0   0:00.74 init
    2 root      15  -5     0    0    0 S    0  0.0   0:00.01 kthreadd
top命令本身提供了一些子命令:
  • (1) h 获得在线帮助
  • (2) q 退出
  • (3) f 从显示中增加或者删除列
  • (4) o 排序
  • (5) F 选择排序的根据列
  • (6) P 按照CPU占用排序
  • (7) M 按照内存占用排序
top - 16:21:48 up 18:43,  5 users,  load average: 0.16, 0.06, 0.01
Tasks: 170 total,   3 running, 167 sleeping,   0 stopped,   0 zombie
Cpu(s):  2.1%us,  0.8%sy,  0.0%ni, 96.6%id,  0.0%wa,  0.0%hi,  0.5%si,  0.0%st
Mem:   4057976k total,  1588940k used,  2469036k free,   195412k buffers
Swap: 10241428k total,        0k used, 10241428k free,   613056k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
 6925 ian       20   0 1171m 338m  21m S    0  8.5   1:44.10 java               
 1381 root      20   0  634m  40m  18m S    0  1.0   2:13.63 Xorg                
 6820 ian       20   0  506m  83m  26m S    3  2.1   0:51.28 firefox            
 2004 ian       20   0  436m  23m  15m S    0  0.6   0:01.55 nautilus           
 2031 ian       20   0  419m  13m  10m S    0  0.3   0:00.11 evolution-alarm    
 2118 ian       20   0  372m  10m 7856 S    0  0.3   0:00.06 evolution-data-    
 2122 ian       20   0  344m  13m  10m S    0  0.3   0:00.10 evolution-excha    
 2001 ian       20   0  331m  22m  14m S    0  0.6   0:13.61 gnome-panel        
 1971 ian       20   0  299m 9.9m 7244 S    0  0.3   0:05.00 gnome-settings-    
 1989 ian       20   0  288m  15m  11m S    0  0.4   0:11.95 metacity           
 1954 ian       20   0  265m 5460 3412 S    0  0.1   0:00.28 pulseaudio  

6 向进程发送信号

现在让我们来看看Linux中的信号。这是进程间异步通信的机制。我们已经提到过SIGHUP这个信号,我们也使用了Ctrl-c和Ctrl-z来发送信号。通用的发送信号的命令是kill。

6.1 使用kill发送信号

kill用来向指定的job或者进程发送信号。下面显示了使用SIGSTP和SIGCONT信号来停止和重启一个后台Job。使用SIGSTP的作用等同于先使用fg把Job带入前台然后使用Ctrl-z来暂停它。SIGCONT信号则等同于bg命令。

ian@attic4:~$ kill -s SIGTSTP %1

[1]+  Stopped                 xclock -d -update 1
ian@attic4:~$ jobs -l
[1]+  3878 Stopped                 xclock -d -update 1
[2]   5485 Running                 nohup sh pmc.sh &
[3]-  5487 Running                 nohup bash pmc.sh &
ian@attic4:~$ kill -s SIGCONT 3878
ian@attic4:~$ jobs -l
[1]   3878 Running                 xclock -d -update 1 &
[2]-  5485 Running                 nohup sh pmc.sh &
[3]+  5487 Running                 nohup bash pmc.sh &

我们使用了%1来指定job,然后使用了PID来重启它。

还有很多其他的信号,可以使用kill-l来查看它们。有些信号用来报告一些错误:非法操作符、浮点异常、非法访问内存等。信号不仅有名字,还有一个对应的数字值,如SIGSTP对应的值是20。可以使用-s 名字或者-数字。比如kill -20与kill -s SIGSTP是一样的。在使用数字前,一定要先确认在你的系统上信号和名字的对应情况,因为这并不是POSIX一致要求的。

6.2 信号处理程序和进程终止

你已经知道了Ctrl-c会终止一个进程。实际上,它是向进程发送了SIGINT信号。如果不带任何信号名,kill会发送SIGTERM信号。很多情况下,这两个信号是等价的。

你也知道了,nohup命令会让一个进程对SIGHUP失效。通常情况下,一个进程可以实现一个信号处理程序来捕获信号。因为信号处理程序能够知道接收到的是什么信号,所以可以做出忽略SIGINT信号,而在接受到SIGERM信号时终止当前进程。下面例子中,我们想job2所在的进程发送了SIGTERM信号,然后这个Job终止了。

ian@attic4:~$ kill -s SIGTERM %2
ian@attic4:~$ 
[2]-  Terminated              nohup sh pmc.sh
ian@attic4:~$ jobs -l
[1]-  3878 Running                 xclock -d -update 1 &
[3]+  5487 Running                 nohup bash pmc.sh &

信号处理程序让进程有了很大的灵活性。一个进程在自己工作的时候,亦可以被信号打断去做一些特殊的事情。信号除了可以用来提醒进程关闭资源外,还用来通知一些服务进程重新读取配置文件初始化。当你改变网络配置后,可能需要发送信号给inetd进程;当你增加一个打印机后,可能需要发送信号给lpd进程。

6.3 强制无条件地结束进程

有些信号是无法被捕获到的,比如一些硬件异常。SIGKILL,这个信号是你最可能使用的,它就不能被信号处理程序捕获,用来无条件的强制结束一个进程。这个信号通常是在其他方法无法结束进程的时候最后的一种方式。

7 登出并且不挂起

我们看到了,nohup命令可以让一个命令在用户登出后继续运行。现在我们先登出然后再登入来看看。重新登录以后,使用jobs和ps来查看继续运行的穷人钟表进程。

ian@attic4:~$ jobs -l
ian@attic4:~$ ps -a
  PID TTY          TIME CMD
10995 pts/0    00:00:00 ps
我们没有发现我们的job,这可不是我们期望的。但是,一切并没有丢失。只是这个进程失去了控制终端了,所以看不到。我们可以先找到bash命令对应的SID,然后看看。
ian@attic4:~$ ps -C bash -C sh -o pid,sid,tname,cmd
  PID   SID TTY      CMD
 5487  2643 ?        bash pmc.sh
 7050  7050 pts/3    -bash
10851 10851 pts/0    bash
ian@attic4:~$ ps -js 2643
  PID  PGID   SID TTY          TIME CMD
 5487  5487  2643 ?        00:00:00 bash
11197  5487  2643 ?        00:00:00 sleep

注意,pmc.sh依旧在运行,只是它的控制终端显示为?。考虑到你已经学习了如何结束一个进程,你应该能够使用kill来结束这个pmc了。

你可能感兴趣的:(Linux相关)