執行個體解析shell子進程(subshell)

來源:互聯網
上載者:User

執行個體解析shell子進程(subshell) 通過執行個體,解析個人對shell子進程的一個瞭解,主要包括以下幾個方面1:什麼是shell子進程2:shell什麼情況下會產生子進程3:子進程的特點與注意事項4:$變數$$在指令碼裡的意義,及如何得到子進程裡的進程號 參考文檔:apue,bash的man和info文檔 1:什麼是shell子進程 子進程,是從父子進程的概念出發的,unix作業系統的進程從init進程開始(init進程為1,而進程號0為系統原始進程,以下討論的進程原則上不包括進程0)均有其對應的子進程,就算是由於父進程先行結束導致的孤兒進程,也會被init領養,使其父進程ID為1。 也因為所有的進程均有父進程,事實上,所有進程的建立,都可視為子進程建立過程。在apue一書裡提及unix作業系統進程的建立,大抵上的模式都是進行fork+exec類系統調用。 理解子進程的建立執行,需要至少細分到二個步驟,包括通過fork建立子進程環境,通過exec載入並執行進程代碼。其間諸如繼承的環境變數等細節,可以查看apue第八章相關章節。 而shell子進程(以下均稱subshell),顧名思義,就是由“當前shell進程”建立的一個子進程 2:shell什麼情況下會產生子進程 以下幾個建立子進程的情況。(以下英文摘自info bash)1:&,提交後台作業If a command is terminated by the control operator `&', the shell executes the command asynchronously in a subshell.2:管道Each command in a pipeline is executed in its own subshell3:括弧命令列表()操作符     Placing a list of commands between parentheses causes a subshell     environment to be created4:執行外部指令碼、程式: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 說明:大致上子進程的建立包括以上四種情況了。需要說明的是只要是符合上邊四種情況之一,便會建立(fork)子進程,不因是否是函數,命令,或程式,也不會因為是內建函數(buitin)或是外部程式。  此外,上邊提到子進程建立與執行的二個步驟,shell子進程的建立在步驟之一併無多大差別,一般還是父進程調用fork產生進程環境,估在第二步exec的時候,是存在差別的。 shell做為解釋語言程式,提供給第二步exec載入和執行的程式體並不是指令碼本身,而是由第一行#!指定的,預設為shell程式,當然也可以是awk,sed等程式,在之前寫過的一篇文章裡:shell指令碼的set id如何生效就有提及。這裡不再展開討論。 只不過子進程的執行會根據情況而有所差別,對於內建函數,exec程式體為shell程式,並在會在子shell直接調用內建函數, 而外部函數或程式,在建立了子進程環境後,大致會有二種執行情況:1:直接exec外部程式,比如下邊例子中直接執行的sleep,pstree命令等2:subshellexec程式體為shell程式,在此基礎上會進一步建立一個子進程以執行函數。比如下邊例子中通過函數提交背景程式中的shell命令等  例:內建函數(直接在subshell裡執行,不管是否通過函數)[root@localhost shell]# mkfifo a[root@localhost shell]# type echoecho is a shell builtin[root@localhost shell]# b(){ echo a>a; }[root@localhost shell]# b &[1] 15697[root@localhost shell]# echo a>a &[2] 15732[root@localhost shell]# pstree -pa $$bash,571  |-bash,15697  |-bash,15732  `-pstree,15734 -pa 571 例:定義函數並提交後台進行(函數調用中的sleep在subshell之下又建立一個子進程,而pstree,sleep命令的直接執行,則是直接在子進程上進行) [root@localhost shell]#  a(){ sleep 30;  } ;[root@localhost shell]# sleep 40 &[1] 15649[root@localhost shell]# a &[2] 15650[root@localhost shell]# pstree -pa $$bash,571  |-bash,15650  |   `-sleep,15651 30  |-pstree,15652 -pa 571  `-sleep,15649 40  對於第四點,要注意,shell指令碼的執行模式,在第四點的二種模式下,shell是會建立子進程的:  filename ARGUMENTSbash filename ARGUMENTS 但shell同時提供二種不建立子程式的進程建立方式1:source命令,使用方法Source   filename ARGUMENTS或.  filename ARGUMENTS 此種方法,直接在當前shell進程中執行filename指令碼,filename結束後繼續返回當前shell進程 2:exec命令,使用方法Exec  filename ARGUMENTS此種方法直接在當前shell進程中執行filname指令碼,filename結束後退出當前shell進程 3:子進程的特點與注意事項這方面不具體展開,只提一點寫指令碼容易出現的錯誤。 做為子進程,其進程環境與父進程的環境是獨立的, 所以在變數傳遞過程中,需要注意子進程內部不能更改到父進程的變數。 比如如下通過管道求和並賦給外部變數sum例子,結果sum值並不會因此改變:[root@localhost shell]# sum=0[root@localhost shell]# echo '1 2 3 4' |sed 's/ //n/g'|while read line; do  sum+=$line; done[root@localhost shell]# echo $sum0[root@localhost shell]#  4:變數$$在指令碼裡的意義 變數$$代表的是當前shell進程的進和id,這裡要特別留意“當前shell”,看看info bash裡的說明`$'     Expands to the process ID of the shell.  In a `()' subshell, it     expands to the process ID of the invoking shell, not the subshell.再看看man bash裡的說明  $     Expands  to  the process ID of the shell.  In a () subshell, it expands to the process ID of the current  shell, not the subshell.  所以在實際環境中,$$並不一定“當前進程”的進程號,而是當前shell進程的進程號。從文檔中,需要留意的便是 invoking shell (info) 或  current  shell(man) 與 當前subshell進程的關係了 這就引出了幾個問題1:到底怎麼樣算是  current  shell2:子進程裡的$$對應的是哪個  current  shell3:如何獵取子進程的$$? 做為調試和測試,下邊的例子引用幾個變數, BASH_SOURCE'     An array variable whose members are the source filenames     corresponding to the elements in the `FUNCNAME' array variable.`BASH_LINENO'     An array variable whose members are the line numbers in source     files corresponding to each member of FUNCNAME.     `${BASH_LINENO[$i]}' is the line number in the source file where     `${FUNCNAME[$i]}' was called.  The corresponding source file name     is `${BASH_SOURCE[$i]}'.  Use `LINENO' to obtain the current line     number.`FUNCNAME'     An array variable containing the names of all shell functions     currently in the execution call stack.  The element with index 0     is the name of any currently-executing shell function.  The     bottom-most element is "main".  This variable exists only when a     shell function is executing.  Assignments to `FUNCNAME' have no     effect and return an error status.  If `FUNCNAME' is unset, it     loses its special properties, even if it is subsequently reset. 指令碼裡set -x,並設定PS4跟蹤程式執行過程 PS4='+[$SHELL][$BASH_SUBSHELL][$PPID-$$][$LINENO]["${BASH_SOURCE[*]}"][${FUNCNAME[*]}][${BASH_LINENO[*]}]/n   +  PS4設定顯示值如下:[$SHELL]:當前shell路徑[$BASH_SUBSHELL]:子shell路徑長度[$PPID-$$]:父進程id,和變數$$值(current  shell進程ID)[$LINENO]:在當前shell的命令列號["${BASH_SOURCE[*]}"]:源指令碼程式檔案記錄隊列[${FUNCNAME[*]}]:函數調用記錄隊列[${BASH_LINENO[*]}]:執行行號記錄隊列   程式如下:[root@localhost shell]# cat -n subshell.sh+[/bin/bash][0][569-571][1060][""][][]   +cat -n subshell.sh     1  #!/bin/bash     2     3  set -x     4  sub2() {     5  #       sh subshell2.sh     6          sleep 1     7  }     8  sub() {     9          sub2 &    10          sleep 20    11  }    12  sub  &    13  pstree -p $PPID  執行結果如下: [root@localhost shell]# bash subshell.sh+[/bin/bash][0][569-571][1059][""][][]   +bash subshell.sh+[/bin/bash][0][571-17858][12]["subshell.sh"][][0]   +sub+[/bin/bash][0][571-17858][13]["subshell.sh"][][0]   +pstree -p 571+[/bin/bash][1][571-17858][10]["subshell.sh subshell.sh"][sub main][12 0]   +sleep 20+[/bin/bash][1][571-17858][9]["subshell.sh subshell.sh"][sub main][12 0]   +sub2+[/bin/bash][2][571-17858][6]["subshell.sh subshell.sh subshell.sh"][sub2 sub main][9 12 0]   +sleep 1bash(571)---bash(17858)-+-bash(17859)-+-bash(17860)---sleep(17863)                        |             `-sleep(17862)                        `-pstree(17861)  說明:1:首先在當前shell(進程id 571)下執行subshell.sh ,產生子進程,[$PPID-$$]=[571-17858]顯示此時執行subshell.sh指令碼的進程號為17858,[][0]顯示未進行函數調用,未產生函數記錄記錄說明在subshell.sh執行進程裡,$$值儲存的“current shell”即為本身,17858 [$LINENO]=[12]顯示在subshell.sh第12行調用sub函數sub函數在程式裡通過&提交後台方式調用,進程樹顯示,sub函數的調用在17858進程後建立子進程17859,執行體為bash此時,ppid指示父進程為571,$$變數值為17858,說明對於sub調用產生的進程,其“current shell”仍然為subshell.sh指令碼執行進程17858 [$LINENO]=[13]顯示在subshell.sh第13行執行pstree命令pstree命令調用方式是在指令碼裡直接調用進程樹顯示,pstree命令直接在17858進程後建立子進程17861並執行此時,ppid指示父進程為571,$$變數值為17858,說明對於這裡啟動並執行pstree命令的子進程,其“current shell”仍然為subshell.sh指令碼執行進程17858  2:[sub main][12 0]顯示進入sub函數內部 [$LINENO]=[9]顯示執行(在sub函數內)指令碼第9行,調用sub2函數進程樹顯示,sub2函數的調用在17859進程後建立子進程17860,執行體為bash此時,ppid仍然指示父進程為571,$$變數值為17858,說明對於sub2調用產生的進程,其“current shell”仍然為subshell.sh指令碼執行進程17858  [$LINENO]=[10]顯示執行(在sub函數內)指令碼第10行,sleep命令此處sleep命令調用方式是在指令碼裡的sub函數內直接調用進程樹顯示,sleep命令是sub函數調用時建立的進程17859後建立子進程17862並執行此時,ppid指示父進程為571,$$變數值為17858,說明對於這裡啟動並執行pstree命令的子進程,其“current shell”仍然為subshell.sh指令碼執行進程17858 3:[sub2 sub main][9 12 0]顯示進入sub2函數內部[6]顯示執行(在sub2函數內)指令碼第6行,sleep 1此處sleep命令調用方式是在指令碼裡的sub2函數內直接調用進程樹顯示,sleep命令是sub2函數調用時建立的進程17860後建立子進程17863並執行此時,ppid指示父進程為571,$$變數值為17858,說明對於這裡啟動並執行sleep命令的子進程,其“current shell”仍然為subshell.sh指令碼執行進程17858 終上這裡的$$只有二個值,一個是最初的bash shell: 571,一個是subshell.sh指令碼調用時產生的進程:17858其他由subshell.sh產生的子進程,無論是函數還是命令運行,$$變數值儲存的“current shell”均為subshell.sh調用時產生的進程:17858 由此推論出上邊提到的四種子shell的建立方法:提交後台,管道,括弧命令列表,指令碼調用。似乎只有第四種方法--指令碼調用--產生的subshell可以做o為“current shell” 可以通過以下二個例子再次論證這個推論例子一:變更指令碼調用方式,此種方式採用當前shell進程執行subshell.sh,不再建立一個子進程結果$$變數值儲存的“current shell”均為當前進程 [root@localhost shell]# source  subshell.sh+[/bin/bash][0][569-571][1062][""][][]   +source subshell.sh++[/bin/bash][0][569-571][3]["subshell.sh"][][1062]   +set -x++[/bin/bash][0][569-571][13]["subshell.sh"][][1062]   +pstree -p 569++[/bin/bash][0][569-571][12]["subshell.sh"][][1062]   +sub++[/bin/bash][1][569-571][1]["subshell.sh subshell.sh"][sub source][12 1062]   +sub2++[/bin/bash][2][569-571][2]["subshell.sh subshell.sh subshell.sh"][sub2 sub source][1 12 1062]  sleep 1++[/bin/bash][1][569-571][2]["subshell.sh subshell.sh"][sub source][12 1062]   +sleep 20sshd(569)---bash(571)-+-bash(18801)---sleep(18804)                      |-bash(18806)---bash(18808)---sleep(18809)                      `-pstree(18807)  例子二:在子進程裡邊,採用指令碼調用方式,或取子進程進程號為此增加一個指令碼subshell2.sh,並在subshell.sh進行調用   +cat -n subshell2.sh     1  #!/bin/bash     2  set -x     3  echo $PPID     4  sleep 10    +cat -n subshell.sh     1  #!/bin/bash     2     3  set -x     4  sub2() {     5          ./subshell2.sh &     6          sleep 1     7  }     8  sub() {     9          sub2 &    10          sleep 20    11  }    12  sub  &    13  pstree -pa $PPID   [root@localhost shell]# ./subshell.sh+[/bin/bash][0][569-571][1138][""][][]   +./subshell.sh+[/bin/bash][0][571-19715][13]["./subshell.sh"][][0]   +pstree -pa 571+[/bin/bash][0][571-19715][12]["./subshell.sh"][][0]   +sub+[/bin/bash][1][571-19715][9]["./subshell.sh ./subshell.sh"][sub main][12 0]   +sub2+[/bin/bash][2][571-19715][5]["./subshell.sh ./subshell.sh ./subshell.sh"][sub2 sub main][9 12 0] ./subshell2.sh+[/bin/bash][2][571-19715][6]["./subshell.sh ./subshell.sh ./subshell.sh"][sub2 sub main][9 12 0] sleep 1+[/bin/bash][1][571-19715][10]["./subshell.sh ./subshell.sh"][sub main][12 0]   +sleep 20+[/bin/bash][0][19718-19719][3]["./subshell2.sh"][][0]   +echo 1971819718+[/bin/bash][0][19718-19719][4]["./subshell2.sh"][][0]   +sleep 10bash,571  `-subshell.sh,19715 ./subshell.sh      |-pstree,19717 -pa 571      `-subshell.sh,19716 ./subshell.sh          |-sleep,19721 20          `-subshell.sh,19718 ./subshell.sh              |-sleep,19720 1              `-subshell2.sh,19719 ./subshell2.sh                  `-sleep,19722 10 從上邊的執行結果可以看出,當程式執行進入subshell2.sh時,建立了進程號19294的進程境,$$變數值儲存的“current shell”均為更新為subshell2.sh調用時建立的進程: 這樣,打出來的subshell2.sh的父進程id,實際上就是sub2調用時建立的進程,這樣就把一些$$本來顯示不出來的子進程id給顯示了出來。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.