jqkyp 发表于 2018-8-21 10:13:48

SHELL父子进程分析

  导言
  本节将就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      513926790 08:52 ?      00:00:00 sshd: work
  work      514351390 08:52 ?      00:00:00 sshd: work@pts/0
  work      514451430 08:52 pts/0    00:00:00 -bash
  work   1025251440 09:07 pts/0    00:00:00 sh test.sh
  work   10253 102520 09:07 pts/0    00:00:00 ping 127.0.0.1
  work   10254 102520 09:07 pts/0    00:00:00 tail -f
  work   10255 102520 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。

页: [1]
查看完整版本: SHELL父子进程分析