分类:
2011-01-10 15:42:23
您可以在自己的命令行,很方便地体验分叉操作。首先,打开一个新的 xterm。(您现在可能会认识到,xterm 就是它本身的进程,在 xterm 中,shell 是由 xterm 产生的一个独立进程)。接下来,输入:
ps -o pid,ppid,uname,command,state,stime,time |
您应该会看到类似这样的内容:
PID PPID USER COMMAND S STIME TIME 16351 16350 mstreic -bash S 11:23 00:00:00 16364 16351 mstreic ps -o pid,ppid,u R 11:24 00:00:00 |
从该列表的 PPID
字段中,我们知道 ps
命令是 bash shell 的子进程。(-bash
中的连字符说明 shell 实例是一个登录 shell。)为了运行 ps
,bash 会分叉,创建一个新进程;新进程通过使用执行,使其本身得以重生,转化为 ps
的一个新的实例。
这里是另一个可供尝试的实验。键入:
sleep 10 & sleep 10 & sleep 10 & ps -o pid,ppid,uname,command,state,stime,time |
您应该会看到类似这样的内容:
$ sleep 10 & sleep 10 & sleep 10 & ps -o pid,ppid,uname,command,state,stime,time PID PPID USER COMMAND S STIME TIME 16351 16350 mstreic -bash S 11:23 00:00:00 16843 16351 mstreic sleep 10 S 11:42 00:00:00 16844 16351 mstreic sleep 10 S 11:42 00:00:00 16845 16351 mstreic sleep 10 S 11:42 00:00:00 16846 16351 mstreic ps -o pid,ppid,u R 11:42 00:00:00 |
命令行生成四个新进程。在每个 sleep
命令后键入 &
,在后台运行每一个命令,或与 Shell 并行。 ps
是生成的另一个进程,但它是在前台运行的,可以防止 shell 在该进程终止之前运行其他命令。而且,如 PPID 的值所示,所有四个进程都是 Shell 的后代。三个 sleep
命令都被标为 S
,因为没有哪个进程会在它们睡眠时使用资源。
为了方便起见,shell 会持续跟踪它生成的所有后台进程。键入 jobs
,可以看到一个列表:
$ sleep 10 & sleep 10 & sleep 10 & [1] 16843 [2] 16844 [3] 16845 $ jobs [1] Running sleep 10 & [2] Running sleep 10 & [3] Running sleep 10 & |
此处,为了方便起见,三个工作分别用标签标为 1,2 和 3。数字 16843、16844 和 16845 分别是每个进程的进程 ID。因此,后台任务 1 即为进程 ID 16843。
您可以利用这些标签,从命令行操作您的后台工作。例如,如要终止某个命令,键入 kill %N
,其中 N
是该命令的标签。如要将某个命令由后台移到前台,请键入 fg %N
:
$ sleep 10 & sleep 10 & sleep 10 & [7] 17741 [8] 17742 [9] 17743 $ kill %7 $ jobs [7] Terminated sleep 10 [8]- Running sleep 10 & [9]+ Running sleep 10 & $ fg %8 sleep 10 |
从命令行中同时异步运行多个命令,是处理您自己的任务集的好方法。一个长时间运行的工作(例如,系统管理的数值计算或大型程序的编译)最适合放在后台。为了捕获每个后台命令的输出,请考虑使用重定向操作符 >
、>&
、>>
和 >>&
,将输入重定向到某个文件。当后台命令结束后,shell 会在下一个提示符之前显示一条警告消息:
$ whoami mstreicher [8]- Done sleep 10 [9]+ Done sleep 10 $ |
某些进程会一直存活(如 init),而某些进程会以新的形式重生(如您的 shell)。最终大多进程都会因自然原因(即程序运行结束)而消亡。
此外,您还可以将某个进程放在一个挂起的动作序列中,等待被再次激活。正如先前的示例所示,您可以用 kill
提前终止某个进程。
当某个命令在前台运行时,如果您希望将它挂起,请按 Ctrl + Z:
$ sleep 10 (Press Control-Z) [1]+ Stopped sleep 10 $ ps PID PPID USER COMMAND S STIME TIME 18195 16351 mstreic sleep 10 T 12:44 00:00:00 |
Shell 已将命令挂起,为了方便起见,还为它分配了一个标签。您可以像先前那样使用这个标签,以终止工作或让工作返回前台。您还可以使用 bg
命令在后台恢复这个进程:
bg %1 [1]+ sleep 10 & |
当某个命令在前台运行时,如果您想终止它,请按 Ctrl + C:
$ sleep 10 (Press Control-C $ jobs $ |
您的 Shell 能使进程的挂起和终止变得更容易,但在 Shell 单纯的外表下,却隐藏着复杂的一面。在内部,Shell 使用 UNIX 信号来影响进程的状态。信号是一个事件,它被用来向某个进程发出警报。操作系统生成许多信号,但您可以将信号从一个进程发送到另一个进程,甚至能让某个进程给自己发送信号。
UNIX 包括多种信号,它们大多都有特殊目的。例如,如果您将信号 SIGSTOP
发送到某个进程,该进程将挂起。(要获取信号的完整列表,请键入 man 7 signal
或键入 kill -L
)。您可以用 kill
命令发送信号。
$ sleep 20 & [1] 19988 $ kill -SIGSTOP 19988 $ jobs [1]+ Stopped sleep 20 |
起初,sleep
命令在后台启动,其进程 ID 为 19988。在发送 SIGSTOP
之后,该进程会改变状态,变为挂起或停止。发送另一个信号 SIGCONT
,重新激活进程,该进程将从上次停止的地方继续执行。
也就是说,每次您按 Ctrl + Z 时,您的 shell 将向前台发送 SIGSTOP
信号。bg
命令发送 SIGCONT
。而 Ctrl + C 则会发送 SIGTERM
,要求立即终止进程。
一些信号可以被某个进程阻塞,应用程序可以通过设计,显式地“捕捉 (catch)”信号,并以一种特殊的方式对每个事件作出反应。例如,系统服务 xinetd 会按需要启动其他网络服务,它在收到 SIGHUP
时会重新读取它的配置文件。在 Linux 中,向 init 发送信号,可能会改变系统的运行级别,甚至会导致系统关闭。.(这里有一个问题:kill %1
和 kill 1
有什么区别?
进程甚至可以给自己发送信号。想像一下,您正在编写一个游戏,想留给用户五秒钟时间作出反应。您的代码可以设置一个五秒钟的定时器,接下来继续进行重绘屏幕等操作。当定时器的时间耗尽后,将有一个 SIGALRM
信号被送回您的进程。呯!时间到!
(这里提供了问题的答案:kill %1
会终止标签为 1 的后台工作。kill 1
会终止 init,当必须关闭计算机时,将向操作系统发送这个信号。)
在特殊情况下,操作系统还可以将一些其他信号传送给进程。内存违例会引发 SIGSEGV
信号,立即终止进程,并留下一个内核转储。有一个特殊的信号 SIGKILL
是无法被阻塞或捕捉的,它会立即终止某个进程。
和 UNIX 中许多其他资源一样,您只能向您拥有的进程发送信号。这可以防止您终止重要的系统服务和其他用户的进程。超级用户 root 可以向任何进程发送信号。