导言
本节将就shell交互(脚本执行)过程中涉及子进程(subprocess)以及子SHELL(subshell)的部分,配以实例,进行说明。将详细讨论如下问题:subprocess和subshell是什么
subprocess的产生过程是什么
什么情况下会产生subprocess和subshell
shell编程中,subshell需要注意些什么
如何管理subprocess
subprocess和subshell是什么
子进程(subporcess)是从父子进程的概念出发的。unix操作系统的进程从init进程开始,经过不断fork-exec“繁衍”,形成了树状的父子进程结构。每个进程均有其对应的父进程(0进程不在讨论范畴内),就算是由于父进程先行结束导致的孤儿进程,也会被init(pid=1)领养,使其父进程ID为1。
子SHELL,顾名思义,就是由“当前shell进程”创建的一个子进程。因此,subshell概念是subprocess的子集,一个subshell一定是个subprocess。
subprocess的产生过程
事实上,所有进程的创建,都可视为子进程创建过程。unix操作系统进程的创建,基本可以归结为fork-exec的模式,即是:通过fork创建子进程环境,
通过exec加载并执行进程代码。
在shell环境中,即是:当前shell fork出一个子进程(即子shell),此时该子shell是父shell的一个副本;
在subshell里,根据path指定的目录列表里的目录,找到外部命令command;
在subshell里,以找到的命令command取代(exec)当前shell程序并执行,此时父shell等待subprocess工作完成;
命令完成后,控制流返回父shell,父shell再取下一条语句执行,或等待用户输入下一条;
什么情况下会产生subshell
&,提交后台作业
If a command is terminated by the control operator `&', the shell executes the command asynchronously in a subshell.
管道
Each command in a pipeline is executed in its own subshell
括号命令列表 ()操作符
Placing a list of commands between parentheses causes a subshell environment to be created
执行外部脚本、程序
When Bash finds such a file while searching the `$PATH' for a command, it spawns a subshell to execute it. In other words, executing filename ARGUMENTS is equivalent to executing bash filename ARGUMENTS
示例
1.shell在子shell中执行外部命令
比如在当前网络终端(通过ssh),键入ps -ef|grep work,看看进程树是如何的:$ echo $$ #得到当前bash的pid 6010
$ pstree -n -a | less #查看进程树
init(1)-+-migration/0(2)
|-ksoftirqd/0(3)
...skipping...
|-sshd(2679)---sshd(5997)---sshd(6009)---bash(6010)-+-pstree(9378)
| `-less(9379)
从结果可以看到,所有进程由init派生而来,2679为sshd系统服务;5997和6009为sshd为work当前终端分配的一个session;6010为基于这个session的、为work用户提供交互操作的shell进程;由于包含管道命令,9378和9379为6010的子进程,即6010(bash)进程spawn了两次。
其中,当键入pstree | less 时,shell先分析pstree是否为非内建命令或别名,结果是外部命令,需要在子进程中执行之,故另启动一个进程(9378)去执行pstree命令;同样,less也按照上述方法执行。
2. 例1的延续
再来看下如果调用sh脚本,系统如何表现:
脚本示例:#! /bin/bash
ping 127.0.0.1 | tail -f | grep time &> /dev/null
$ sh test.sh
$ pstree -n -a | less #查看进程树
|-sshd(2679)-+-sshd(5139)---sshd(5143)---bash(5144)---sh(10252)-+-ping(10253)
| | |-tail(10254)
| | `-grep(10255)
root 5139 2679 0 08:52 ? 00:00:00 sshd: work [priv]
work 5143 5139 0 08:52 ? 00:00:00 sshd: work@pts/0
work 5144 5143 0 08:52 pts/0 00:00:00 -bash
work 10252 5144 0 09:07 pts/0 00:00:00 sh test.sh
work 10253 10252 0 09:07 pts/0 00:00:00 ping 127.0.0.1
work 10254 10252 0 09:07 pts/0 00:00:00 tail -f
work 10255 10252 0 09:07 pts/0 00:00:00 grep tome
可见,当sh test.sh的时候,当前bash6010另启动一个subshell(10252)去执行test.sh内的语句,之后的语句可视作把10252作为“执行test.sh的主干道”,ping命令又作为10253的子进程执行,直到test.sh最后一句执行完毕,10252才结束并返回给交互式bash6010。