天天看點

【原創】Linux 下程式背景運作相關問題總結基于sleep的小實驗部署工具腳本中的問題

千言萬語,不如實驗來的直接... 

      首先通過實驗直覺感受一下背景服務的運作狀況(請注意,前方高能,相關概念在更後面才有解釋)。 

确定登入 shell 和僞終端。 

<a href="http://my.oschina.net/moooofly/blog/498924#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

<code>[root@yoyo ~]</code><code># ps ajxf         </code>

<code> </code><code>ppid   pid  pgid   sid tty      tpgid stat   uid   time command</code>

<code>...</code>

<code>    </code><code>0     1     1     1 ?           -1 ss       0   0:02</code><code>/sbin/init</code>

<code>    </code><code>1  1654  1654  1654 ?           -1 ss       0   0:00</code><code>/usr/sbin/sshd</code>

<code> </code><code>1654 15437 15437 15437 ?           -1 ss       0   0:00  \_ sshd: root@pts</code><code>/0</code><code>,pts</code><code>/1</code>

<code>15437 15441 15441 15441 pts</code><code>/0</code>    <code>15441 ss+      0   0:00      \_ -</code><code>bash</code>

<code>15437 16237 16237 16237 pts</code><code>/1</code>    <code>16258 ss       0   0:00      \_ -</code><code>bash</code>

<code>16237 16258 16258 16237 pts</code><code>/1</code>    <code>16258 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

<code>[root@yoyo ~]</code><code>#</code>

分别以背景方式(&amp;)、setsid、nohup 和前台方式執行 sleep  

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<code>[root@yoyo ~]</code><code># jobs -l         </code>

<code>[root@yoyo ~]</code><code># sleep 600 &amp;        -- 通過 &amp; 背景運作   -- 1</code>

<code>[1] 16261</code>

<code>[root@yoyo ~]</code><code># setsid sleep 660   -- 通過 setsid 背景運作   -- 2</code>

<code>[root@yoyo ~]</code><code># nohup sleep 720 &amp;   -- 通過 nohup + &amp; 背景運作   -- 3</code>

<code>[2] 16271</code>

<code>[root@yoyo ~]</code><code># nohup: 忽略輸入并把輸出追加到"nohup.out"</code>

<code>[root@yoyo ~]</code><code># sleep 780        -- 前台運作   -- 4</code>

<code>^z                              -- 挂起</code>

<code>[3]+  stopped                </code><code>sleep</code> <code>780</code>

<code>[root@yoyo ~]</code><code># jobs -l</code>

<code>[1]  16261 running                </code><code>sleep</code> <code>600 &amp;</code>

<code>[2]- 16271 running                </code><code>nohup</code> <code>sleep</code> <code>720 &amp;</code>

<code>[3]+ 16274 停止                 </code><code>sleep</code> <code>780</code>

<code>[root@yoyo ~]</code><code># bg 3             -- 放入背景運作</code>

<code>[3]+</code><code>sleep</code> <code>780 &amp;</code>

<code>[3]+ 16274 running                </code><code>sleep</code> <code>780 &amp;</code>

檢視此時的程序關系 

<code>15437 16237 16237 16237 pts</code><code>/1</code>    <code>16282 ss       0   0:00      \_ -</code><code>bash</code>

<code>16237 16261 16261 16237 pts</code><code>/1</code>    <code>16282 s        0   0:00          \_</code><code>sleep</code> <code>600       -- 1</code>

<code>16237 16271 16271 16237 pts</code><code>/1</code>    <code>16282 s        0   0:00          \_</code><code>sleep</code> <code>720       -- 3</code>

<code>16237 16274 16274 16237 pts</code><code>/1</code>    <code>16282 s        0   0:00          \_</code><code>sleep</code> <code>780       -- 4</code>

<code>16237 16282 16282 16237 pts</code><code>/1</code>    <code>16282 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

<code>    </code><code>1 16265 16265 16265 ?           -1 ss       0   0:00</code><code>sleep</code> <code>660         -- 2</code>

叉掉 ssh 連接配接視窗,檢視此時的 sleep 程序狀态 

<code>[root@yoyo ~]</code><code># ps ajxf</code>

<code> </code><code>1654 15437 15437 15437 ?           -1 ss       0   0:00  \_ sshd: root@pts</code><code>/0</code>

<code>15437 15441 15441 15441 pts</code><code>/0</code>    <code>16300 ss       0   0:00      \_ -</code><code>bash</code>

<code>15441 16300 16300 15441 pts</code><code>/0</code>    <code>16300 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

<code>    </code><code>1 16265 16265 16265 ?           -1 ss       0   0:00</code><code>sleep</code> <code>660      -- 2</code>

<code>    </code><code>1 16271 16271 16237 ?           -1 s        0   0:00</code><code>sleep</code> <code>720      -- 3</code>

實驗結論: 

以不同方式啟動程序,在 ssh 連接配接視窗被叉掉的時候會造成不同的影響。标号為 1 和 4 的兩個程序都消失了,标号為 3 的程序有屬性發生了變化,隻有标号為 2 的程序沒有任何改變。 

測試一(前台程序組) 

<code>[root@betty ~]</code><code># vi test_1.sh</code>

<code> </code> 

<code>#!/bin/sh</code>

<code>sleep</code> <code>600       </code><code># 會卡住目前 shell 腳本</code>

<code>[root@betty ~]</code><code>#</code>

<code>[root@betty ~]</code><code># ./test_1.sh</code>

<code>(卡住)</code>

 在另一個視窗中檢視 

<code>[root@betty ~]</code><code># ps ajxf</code>

<code>    </code><code>1  1860  1860  1860 ?           -1 ss       0   0:00</code><code>/usr/sbin/sshd</code>

<code> </code><code>1860 13331 13331 13331 ?           -1 ss       0   0:00  \_ sshd: root@pts</code><code>/1</code><code>,pts</code><code>/2</code>

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18649 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18649 18649 16993 pts</code><code>/1</code>    <code>18649 r+       0   0:00      |   \_</code><code>ps</code> <code>ajxf</code>

<code>13331 18572 18572 18572 pts</code><code>/2</code>    <code>18632 ss       0   0:00      \_ -</code><code>bash</code>

<code>18572 18632 18632 18572 pts</code><code>/2</code>    <code>18632 s+       0   0:00          \_</code><code>/bin/sh</code> <code>.</code><code>/test_1</code><code>.sh</code>

<code>18632 18633 18632 18572 pts</code><code>/2</code>    <code>18632 s+       0   0:00              \_</code><code>sleep</code> <code>600</code>

此時叉掉啟動 test_1.sh 腳本的視窗,可以看到對應的程序全部消失。 

<code> </code><code>1860 13331 13331 13331 ?           -1 ss       0   0:00  \_ sshd: root@pts</code><code>/1</code>

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18706 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18706 18706 16993 pts</code><code>/1</code>    <code>18706 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

測試二(孤兒背景程序組) 

<code>[root@betty ~]</code><code># vi test_2.sh</code>

<code>sleep</code> <code>600 &amp;        </code><code># 不會卡住目前 shell 腳本,因為放在背景執行</code>

<code>  </code> 

<code>[root@betty ~]</code><code># ./test_2.sh</code>

在另一個視窗中檢視 

<code> </code><code>1860 13331 13331 13331 ?           -1 ss       0   0:00  \_ sshd: root@pts</code><code>/1</code><code>,pts</code><code>/0</code>

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18778 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18778 18778 16993 pts</code><code>/1</code>    <code>18778 r+       0   0:00      |   \_</code><code>ps</code> <code>ajxf</code>

<code>13331 18734 18734 18734 pts</code><code>/0</code>    <code>18734 ss+      0   0:00      \_ -</code><code>bash</code>

<code>1 18763 18762 18734 pts</code><code>/0</code>    <code>18734 s        0   0:00</code><code>sleep</code> <code>600  -- 對應背景執行</code><code>sleep</code> <code>的程序,由于是背景執行,</code>

<code>                                                                   </code><code>是以不會卡住</code><code>test</code><code>.sh 腳本的執行</code>

<code>                                                                   </code><code>test</code><code>.sh 腳本執行結束後,與</code><code>test</code><code>.sh 對應的程序</code>

<code>                                                                   </code><code>會自行退出,進而</code><code>sleep</code> <code>程序被 init 程序收養</code>

此時叉掉啟動 test_2.sh 腳本的視窗,可以看到 sleep 600 對應程序的 tty 和 tpgid 發生了變化,但程序并未消失。 

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18816 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18816 18816 16993 pts</code><code>/1</code>    <code>18816 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

<code>    </code><code>1 18763 18762 18734 ?           -1 s        0   0:00</code><code>sleep</code> <code>600</code>

測試三(前台程序組) 

<code>[root@betty ~]</code><code># vi test_3.sh</code>

<code>sleep</code> <code>600 &amp;</code>

<code>sleep</code> <code>720</code>

<code>[root@betty ~]</code><code># ./test_3.sh</code>

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18918 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18918 18918 16993 pts</code><code>/1</code>    <code>18918 r+       0   0:00      |   \_</code><code>ps</code> <code>ajxf</code>

<code>13331 18856 18856 18856 pts</code><code>/2</code>    <code>18908 ss       0   0:00      \_ -</code><code>bash</code>

<code>18856 18908 18908 18856 pts</code><code>/2</code>    <code>18908 s+       0   0:00          \_</code><code>/bin/sh</code> <code>.</code><code>/test_3</code><code>.sh</code>

<code>18908 18909 18908 18856 pts</code><code>/2</code>    <code>18908 s+       0   0:00              \_</code><code>sleep</code> <code>600</code>

<code>18908 18910 18908 18856 pts</code><code>/2</code>    <code>18908 s+       0   0:00              \_</code><code>sleep</code> <code>720</code>

 此時叉掉啟動 test_3.sh 腳本的視窗,可以看到對應的程序全部消失。  

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>18963 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 18963 18963 16993 pts</code><code>/1</code>    <code>18963 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

測試四(背景程序組) 

<code>[root@betty ~]</code><code># cat test_1.sh</code>

<code>sleep</code> <code>600</code>

<code>[root@betty ~]</code><code># ./test_1.sh &amp;   -- 背景執行該腳本</code>

<code>[1] 19016</code>

 在另外一個視窗中檢視 

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>19032 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 19032 19032 16993 pts</code><code>/1</code>    <code>19032 r+       0   0:00      |   \_</code><code>ps</code> <code>ajxf</code>

<code>13331 18993 18993 18993 pts</code><code>/0</code>    <code>18993 ss+      0   0:00      \_ -</code><code>bash</code>

<code>18993 19016 19016 18993 pts</code><code>/0</code>    <code>18993 s        0   0:00          \_</code><code>/bin/sh</code> <code>.</code><code>/test_1</code><code>.sh</code>

<code>19016 19017 19016 18993 pts</code><code>/0</code>    <code>18993 s        0   0:00              \_</code><code>sleep</code> <code>600</code>

此時叉掉背景啟動 test_1.sh 腳本的視窗,可以看到對應的程序全部消失。 

<code>13331 16993 16993 16993 pts</code><code>/1</code>    <code>19052 ss       0   0:00      \_ -</code><code>bash</code>

<code>16993 19052 19052 16993 pts</code><code>/1</code>    <code>19052 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

如果 shell 腳本中存在前台執行的指令,則在其未執行結束前,會“卡住”目前 shell 腳本對應的程序,進而“卡住”bash 程序。即整個程序樹在 ps ajxf 中都會作為前台程序組顯示。

&amp; 的使用在 shell 腳本内外會産生不同的效果,在 shell 腳本内可以産生孤兒程序組(前提是沒有其他指令的執行導緻 shell 腳本無法退出),在腳本外則産生普通的背景程序組;

對于孤兒程序組和普通背景程序組 sighup 信号在處理細節上是不同的;

要想了解上面的實驗結果,首先必須了解如下一些概念:

【程序組】 

一個或多個程序的集合。通常與同一作業(job)相關聯。

每個程序組都可以有一個組長程序,組長程序的特征是“程序組 id 等于其程序 id”,即pgid = pid。

組長程序自身可以在建立一個程序組後,再建立該程序組中的其他程序,然後終止自己。

隻要在某個程序組中有一個程序存在,則該程序組就存在,這與其組長程序是否終止無關。

程序可以通過調用 setpgid 來加入一個現有的程序組或者建立一個新程序組(也可以通過 setsid 建立一個新的程序組)。

一個程序隻能為它自己或它的子程序設定程序組 id。

【會話】 

posix.1 引入會話(session)的概念。

登入 shell 是一個會話的開始,而終端或僞終端則是會話的控制終端。

會話是一個或多個程序組的集合。

程序通過調用 setsid 函數建立一個新會話。

如果調用 setsid 函數的程序不是一個程序組的組長,則調用 setsid 函數就會建立一個新會話,同時發生下面 3 件事,

(a) 該程序變成會話首程序(session leader)(會話首程序是建立該會話的程序);

(b) 該程序成為一個新程序組的組長程序。新程序組 id 是該調用程序的程序 id;

(c) 該程序沒有控制終端,如果在調用 setsid 之前,該程序有一個控制終端,那麼這種聯系也會被中斷。

如果調用此函數的程序已經是一個程序組的組長,則此函數傳回出錯。為了保證不會發生這種情況,通常先調用 fork,然後使其父程序終止,而子程序則繼續。因為子程序繼承了父程序的程序組 id,而其程序 id 則是新配置設定的,兩者不可能相等,是以就保證了子程序不會是一個程序組的組長。

會話首程序總是一個程序組的組長程序,是以兩者是等價的。是以可以認為

會話首程序id = 會話id = 程序組id = 程序組組長id 

一個會話中包含的多個程序組可以被分成一個前台程序組(foreground process group)以及一個或幾個背景程序組(background process group)。

【登陸shell】 

當通過終端或網絡登入時,可以得到一個登入 shell,其标準輸入、輸出和标準出錯将連接配接到一個終端裝置或者僞終端裝置上。 

<code> </code><code>1860  2228  2228  2228 ?           -1 ss       0   0:02  \_ sshd: root@pts</code><code>/1</code><code>,pts</code><code>/2</code><code>,pts</code><code>/3</code><code>,pts</code><code>/4</code><code>,pts</code><code>/0</code><code>,pts</code><code>/5</code><code>,pts</code><code>/6</code><code>,pts</code><code>/7</code><code>,pts</code><code>/8</code><code>,pts</code><code>/9</code>

<code> </code><code>2228  2230  2230  2230 pts</code><code>/1</code>     <code>2230 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228  2747  2747  2747 pts</code><code>/2</code>     <code>2747 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228  2772  2772  2772 pts</code><code>/3</code>     <code>2772 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228  6750  6750  6750 pts</code><code>/4</code>    <code>16910 ss       0   0:00      \_ -</code><code>bash</code>

<code> </code><code>6750 16910 16910  6750 pts</code><code>/4</code>    <code>16910 sl+      0   0:02      |   \_ .</code><code>/modb</code> <code>/etc/modbcore</code><code>.conf</code>

<code> </code><code>2228  7213  7213  7213 pts</code><code>/0</code>     <code>7213 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228 17072 17072 17072 pts</code><code>/5</code>    <code>17072 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228 17091 17091 17091 pts</code><code>/6</code>    <code>17091 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228 17111 17111 17111 pts</code><code>/7</code>    <code>17111 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228 17132 17132 17132 pts</code><code>/8</code>    <code>17132 ss+      0   0:00      \_ -</code><code>bash</code>

<code> </code><code>2228 17154 17154 17154 pts</code><code>/9</code>    <code>17175 ss       0   0:00      \_ -</code><code>bash</code>

<code>17154 17175 17175 17154 pts</code><code>/9</code>    <code>17175 r+       0   0:00          \_</code><code>ps</code> <code>ajxf</code>

      可以看到,上面所有 -bash 均為通過 ssh 網絡連接配接建立的登陸 shell,且都對應到 pts 僞終端上,即登陸 shell 擁有控制終端。 

      另外,可以看到登陸 shell 的 pid = pgid = sid,是以登陸 shell 就是會話首程序,以及程序組組長程序。 

      登陸 shell 是一個 posix.1 會話的開始,而此終端或僞終端則是會話的控制終端。

【僞終端】 

      為使同一個軟體既能處理終端 login,又能處理網絡 login,系統使用了一種稱為僞終端的軟體驅動程式,它仿真串行終端的運作行為,并将終端操作映射為網絡操作,反之亦然。 

      當通過終端(基于硬連結和終端裝置驅動程式)或網絡(基于網絡連接配接和僞終端裝置驅動程式)登入時,我們得到一個登入 shell,其标準輸入、輸出和标準出錯連接配接到一個終端裝置或者僞終端裝置上。 

<code>[root@yoyo ~]</code><code># w</code>

<code> </code><code>14:58:20 up 2 days,  5:30,  1 user,  load average: 0.00, 0.00, 0.00</code>

<code>user     tty      from              login@   idle   jcpu   pcpu what</code>

<code>root     pts</code><code>/1</code>    <code>172.16.81.112    mon09    0.00s  0.45s  0.07s w</code>

<code>[root@yoyo ~]</code><code># ps aux|grep bash</code>

<code>root      2030  0.0  0.0 108552  2000 pts</code><code>/1</code>    <code>ss   jul13   0:00 -</code><code>bash</code>

<code>root     24962  0.0  0.0 103256   856 pts</code><code>/1</code>    <code>s+   14:58   0:00</code><code>grep</code> <code>bash</code>

<code>[root@yoyo ~]</code><code># ll /proc/2030/fd</code>

<code>總用量 0</code>

<code>lrwx------. 1 root root 64 7月  13 09:43 0 -&gt;</code><code>/dev/pts/1</code>

<code>lrwx------. 1 root root 64 7月  13 09:43 1 -&gt;</code><code>/dev/pts/1</code>

<code>lrwx------. 1 root root 64 7月  13 09:43 2 -&gt;</code><code>/dev/pts/1</code>

<code>lrwx------. 1 root root 64 7月  15 11:04 255 -&gt;</code><code>/dev/pts/1</code>

【控制終端】 

一個會話可以有一個控制終端(controlling terminal),這通常是登陸到其上的終端裝置(終端登陸)或僞終端裝置(網絡登入)。

隻有建立與控制終端連接配接的會話首程序被稱為控制程序(controlling process)。

如果一個會話有一個控制終端,則它有一個前台程序組,會話中的其他程序組則為背景程序組。

無論何時鍵入終端的中斷鍵(ctrl+c),就會将中斷信号 sigint 發送給前台程序組中的所有程序;

無論何時鍵入終端的退出鍵(ctrl+\),就會将退出信号 sigquit 發送給前台程序組中的所有程序;

如果終端接口檢測到數據機或網路已經斷開連接配接,則将挂斷信号 sighup 發送給控制程序(會話首程序)。

如果在調用setsid之前某程序有一個控制終端,那麼在調用後該控制終端會被中斷。

【作業控制】 

作業控制是 bsd 在 1980 年前後增加的一個特性。它允許在一個終端上啟動多個作業(程序組),它控制哪一個作業可以通路該終端,以及哪些作業在背景運作。 

【信号】 

sigttou - 背景作業試圖輸出到控制終端,若使用者設定了禁止背景作業寫到控制終端(stty tostop),終端驅動程式會将該寫操作辨別為來自于背景程序,會向其發送該信号。

sigttin - 背景作業試圖讀取控制終端,終端驅動程式發現後,會向背景作業發送該信号。

sigtstp - 鍵入ctrl+z挂起鍵與終端驅動程式進行互動,令其将該信号送至前台程序組中的所有程序,背景程序組作業不受影響。

sighup - 當檢測到來自控制終端的 hangup 信号時,或者當控制程序死亡時 (signal(7))

【守護程序】 

守護程序也稱為 daemon,是生存期較長的一種程序。常常在系統自舉時啟動,僅在系統關閉時才終止。沒有控制終端,在背景運作。

大多數守護程序都以超級使用者(uid 為 0)特權運作。

守護程序沒有控制終端,ps 輸出時,其控制終端顯示為問号(?),前台程序組 id 為 -1。

核心守護程序一般會以無控制終端方式啟動;而使用者實作的守護程序若沒有控制終端,則可能是因為建立守護程序時調用了 setsid。

大多數守護程序的父程序是 init 程序。

【孤兒程序組】 

一個其父程序已經終止的程序稱為孤兒程序,這種程序會被 init 程序“收養”;

posix.1 将孤兒程序組定義為:該程序組中每個成員的父程序要麼是該組的一個成員,要麼不是該組所屬會話的成員;

對孤兒程序組的另一種描述為:一個程序組不是孤兒程序組的條件是,該組中有一個程序,其父程序在屬于同一會話的另一個組中。

若父程序退出導緻程序組成為孤兒程序組,且該程序組中有程序處于停止狀态(收到 sigstop 或 sigtstp 信号後被挂起),信号 sighup 會被發送到該程序組中的每一個被挂起的程序。

系統對 sighup 信号的預設處理是終止收到該信号的程序。是以若程式中沒有捕捉該信号,當收到該信号時,程序就會退出;

終端(或僞終端)被關閉時,信号 sighup 會被核心發送到具有控制終端的會話的會話首程序;

會話首程序退出前,信号 sighup 會被核心發送到目前會話中的前台程序組中的每一個程序;

bash 收到 sighup 時,會給其下運作的各個作業(包括前背景)發送 sighup,然後自己退出;

前背景的各個作業收到來自 bash 的 sighup 後将退出(如果存在針對 sighup 的處理,就不會退出)

      既然知道了程序消失是因為 sighup 信号導緻,那麼就可以通過 strace 觀察各種運作方式下,都做了哪些相關處理。 

跟蹤前台運作,可以看到其中沒有針對 sighup 信号做任何處理。 

30

31

32

33

34

35

<code>[root@yoyo ~]</code><code># strace sleep 10</code>

<code>execve(</code><code>"/bin/sleep"</code><code>, [</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = 0</code>

<code>brk(0)                                  = 0xafa000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fe54ec09000</code>

<code>access(</code><code>"/etc/ld.so.preload"</code><code>, r_ok)      = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>open</code><code>(</code><code>"/etc/ld.so.cache"</code><code>, o_rdonly)      = 3</code>

<code>fstat(3, {st_mode=s_ifreg|0644, st_size=70566, ...}) = 0</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7fe54ebf7000</code>

<code>close(3)                                = 0</code>

<code>open</code><code>(</code><code>"/lib64/libc.so.6"</code><code>, o_rdonly)      = 3</code>

<code>read</code><code>(3,</code><code>"\177elf\2\1\1\3\0\0\0\0\0\0\0\0\3\0&gt;\0\1\0\0\0p\356\0018?\0\0\0"</code><code>..., 832) = 832</code>

<code>fstat(3, {st_mode=s_ifreg|0755, st_size=1926760, ...}) = 0</code>

<code>mmap(0x3f38000000, 3750152, prot_read|prot_exec, map_private|map_denywrite, 3, 0) = 0x3f38000000</code>

<code>mprotect(0x3f3818a000, 2097152, prot_none) = 0</code>

<code>mmap(0x3f3838a000, 20480, prot_read|prot_write, map_private|map_fixed|map_denywrite, 3, 0x18a000) = 0x3f3838a000</code>

<code>mmap(0x3f3838f000, 18696, prot_read|prot_write, map_private|map_fixed|map_anonymous, -1, 0) = 0x3f3838f000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fe54ebf6000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fe54ebf5000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fe54ebf4000</code>

<code>arch_prctl(arch_set_fs, 0x7fe54ebf5700) = 0</code>

<code>mprotect(0x3f3838a000, 16384, prot_read) = 0</code>

<code>mprotect(0x3f37a1f000, 4096, prot_read) = 0</code>

<code>munmap(0x7fe54ebf7000, 70566)           = 0</code>

<code>brk(0xb1b000)                           = 0xb1b000</code>

<code>open</code><code>(</code><code>"/usr/lib/locale/locale-archive"</code><code>, o_rdonly) = 3</code>

<code>fstat(3, {st_mode=s_ifreg|0644, st_size=99154480, ...}) = 0</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7fe548d64000</code>

<code>nanosleep({10, 0}, null)                = 0                -- 對應</code><code>sleep</code> <code>10</code>

<code>close(1)                                = 0</code>

<code>close(2)                                = 0</code>

<code>exit_group(0)                           = ?</code>

跟蹤背景運作,可以看到其中同樣沒有針對sighup信号做任何處理。 

36

37

38

<code>[root@yoyo ~]</code><code># strace sleep 10 &amp;</code>

<code>[1] 2727</code>

<code>[root@yoyo ~]</code><code># execve("/bin/sleep", ["sleep", "10"], [/* 28 vars */]) = 0</code>

<code>brk(0)                                  = 0x1406000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7ff18790f000</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7ff1878fd000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7ff1878fc000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7ff1878fb000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7ff1878fa000</code>

<code>arch_prctl(arch_set_fs, 0x7ff1878fb700) = 0</code>

<code>munmap(0x7ff1878fd000, 70566)           = 0</code>

<code>brk(0x1427000)                          = 0x1427000</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7ff181a6a000</code>

<code>nanosleep({10, 0}, null)                = 0              -- 對應</code><code>sleep</code> <code>10</code>

<code>[1]+  done                   </code><code>strace</code> <code>sleep</code> <code>10</code>

跟蹤 setsid 的使用,可以看到其中同樣沒有針對 sighup 信号做任何處理(通過 setsid 執行後不會退出的原因後續再說明)。 

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

<code>[root@yoyo ~]</code><code># strace setsid sleep 10       </code>

<code>execve(</code><code>"/usr/bin/setsid"</code><code>, [</code><code>"setsid"</code><code>,</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = 0</code>

<code>brk(0)                                  = 0x1274000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f2dc78d4000</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7f2dc78c2000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f2dc78c1000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f2dc78c0000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f2dc78bf000</code>

<code>arch_prctl(arch_set_fs, 0x7f2dc78c0700) = 0</code>

<code>munmap(0x7f2dc78c2000, 70566)           = 0</code>

<code>brk(0x1295000)                          = 0x1295000</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7f2dc1a2f000</code>

<code>getpgrp()                               = 2743              -- 擷取程序組</code><code>id</code>

<code>getpid()                                = 2744              -- 擷取程序</code><code>id</code>

<code>setsid()                                = 2744              -- 建立新的會話</code>

<code>execve(</code><code>"/usr/lib64/qt-3.3/bin/sleep"</code><code>, [</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>execve(</code><code>"/usr/local/sbin/sleep"</code><code>, [</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>execve(</code><code>"/usr/local/bin/sleep"</code><code>, [</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>execve(</code><code>"/sbin/sleep"</code><code>, [</code><code>"sleep"</code><code>,</code><code>"10"</code><code>], [/* 28 vars */]) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>brk(0)                                  = 0x1f96000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f60207fc000</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7f60207ea000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f60207e9000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f60207e8000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f60207e7000</code>

<code>arch_prctl(arch_set_fs, 0x7f60207e8700) = 0</code>

<code>munmap(0x7f60207ea000, 70566)           = 0</code>

<code>brk(0x1fb7000)                          = 0x1fb7000</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7f601a957000</code>

<code>nanosleep({10, 0}, null)                = 0                 -- 對應</code><code>sleep</code> <code>10</code>

跟蹤 nohup 的使用,可以看到内部設定了對 sighup 信号的忽略處理。 

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

<code>[root@yoyo ~]</code><code># strace nohup sleep 10 &amp;</code>

<code>[1] 763</code>

<code>[root@yoyo ~]</code><code># execve("/usr/bin/nohup", ["nohup", "sleep", "10"], [/* 28 vars */]) = 0</code>

<code>brk(0)                                  = 0x138b000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f41c123d000</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7f41c122b000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f41c122a000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f41c1229000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f41c1228000</code>

<code>arch_prctl(arch_set_fs, 0x7f41c1229700) = 0</code>

<code>munmap(0x7f41c122b000, 70566)           = 0</code>

<code>brk(0x13ac000)                          = 0x13ac000</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7f41bb398000</code>

<code>ioctl(0, sndctl_tmr_timebase or tcgets, {b38400 opost isig -icanon -</code><code>echo</code> <code>...}) = 0</code>

<code>ioctl(1, sndctl_tmr_timebase or tcgets, {b38400 opost isig -icanon -</code><code>echo</code> <code>...}) = 0</code>

<code>ioctl(2, sndctl_tmr_timebase or tcgets, {b38400 opost isig -icanon -</code><code>echo</code> <code>...}) = 0</code>

<code>open</code><code>(</code><code>"/dev/null"</code><code>, o_wronly)             = 3                      -- 以“隻寫”權限打開</code><code>/dev/null</code>

<code>dup2(3, 0)                              = 0                      -- 将标準輸入重定向到</code><code>/dev/null</code>

<code>umask</code><code>(037777777177)                     = 022</code>

<code>open</code><code>(</code><code>"nohup.out"</code><code>, o_wronly|o_creat|o_append, 0600) = 3           -- 打開</code><code>nohup</code><code>.out 檔案</code>

<code>dup2(3, 1)                              = 1                      -- 将标準輸出重定向到</code><code>nohup</code><code>.out</code>

<code>umask</code><code>(022)                              = 0177</code>

<code>open</code><code>(</code><code>"/usr/share/locale/locale.alias"</code><code>, o_rdonly) = 3</code>

<code>fstat(3, {st_mode=s_ifreg|0644, st_size=2512, ...}) = 0</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7f41c123c000</code>

<code>read</code><code>(3,</code><code>"# locale name alias data base.\n#"</code><code>..., 4096) = 2512</code>

<code>read</code><code>(3,</code><code>""</code><code>, 4096)                       = 0</code>

<code>munmap(0x7f41c123c000, 4096)            = 0</code>

<code>open</code><code>(</code><code>"/usr/share/locale/zh_cn.utf-8/lc_messages/coreutils.mo"</code><code>, o_rdonly) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>open</code><code>(</code><code>"/usr/share/locale/zh_cn.utf8/lc_messages/coreutils.mo"</code><code>, o_rdonly) = -1 enoent (no such</code><code>file</code> <code>or directory)</code>

<code>open</code><code>(</code><code>"/usr/share/locale/zh_cn/lc_messages/coreutils.mo"</code><code>, o_rdonly) = 3</code>

<code>fstat(3, {st_mode=s_ifreg|0644, st_size=286636, ...}) = 0</code>

<code>mmap(null, 286636, prot_read, map_private, 3, 0) = 0x7f41bb352000</code>

<code>open</code><code>(</code><code>"/usr/lib64/gconv/gconv-modules.cache"</code><code>, o_rdonly) = 3</code>

<code>fstat(3, {st_mode=s_ifreg|0644, st_size=26060, ...}) = 0</code>

<code>mmap(null, 26060, prot_read, map_shared, 3, 0) = 0x7f41bb34b000</code>

<code>write(2,</code><code>"nohup: "</code><code>, 7nohup: )                  = 7</code>

<code>write(2,</code><code>"\345\277\275\347\225\245\350\276\223\345\205\245\345\271\266\346\212\212\350\276\223\345\207\272\350\277\275\345\212\240\345\210"</code><code>..., 44忽略輸入并把輸出追加到</code><code>"nohup.out"</code><code>) = 44</code>

<code>write(2,</code><code>"\n"</code><code>, 1</code>

<code>)                       = 1</code>

<code>fcntl(2, f_dupfd, 3)                    = 3</code>

<code>fcntl(3, f_getfd)                       = 0</code>

<code>fcntl(3, f_setfd, fd_cloexec)           = 0</code>

<code>dup2(1, 2)                              = 2                       -- 将标準出錯重定向到 nohuo.out</code>

<code>rt_sigaction(sighup, {sig_ign, [hup], sa_restorer|sa_restart, 0x3f380326a0}, {sig_dfl, [], 0}, 8) = 0     -- 設定 sighup 信号處理函數為 sig_ign</code>

<code>brk(0)                                  = 0x86a000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fc801ecc000</code>

<code>mmap(null, 70566, prot_read, map_private, 3, 0) = 0x7fc801eba000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fc801eb9000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fc801eb8000</code>

<code>mmap(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0x7fc801eb7000</code>

<code>arch_prctl(arch_set_fs, 0x7fc801eb8700) = 0</code>

<code>munmap(0x7fc801eba000, 70566)           = 0</code>

<code>brk(0x88b000)                           = 0x88b000</code>

<code>mmap(null, 99154480, prot_read, map_private, 3, 0) = 0x7fc7fc027000</code>

<code>nanosleep({10, 0}, null)                = 0                  -- 對應</code><code>sleep</code> <code>10</code>

<code>[1]+  done                   </code><code>strace</code> <code>nohup</code> <code>sleep</code> <code>10</code>

      通過上面的 strace 輸出沒有看出,為何通過 setsid 方式啟動的程式不會因為 sighup 信号退出的原因(但從理論上講,我們知道是因為建立的程序沒有控制終端的緣故)。下面看一下這兩指令的源碼實作。 

下面是 setsid 的核心源碼(取自 util-linux-2.26) 

【原創】Linux 下程式背景運作相關問題總結基于sleep的小實驗部署工具腳本中的問題

下面給出 nohup 的核心源碼(取自 coreutils-8.24) 

【原創】Linux 下程式背景運作相關問題總結基于sleep的小實驗部署工具腳本中的問題
【原創】Linux 下程式背景運作相關問題總結基于sleep的小實驗部署工具腳本中的問題
【原創】Linux 下程式背景運作相關問題總結基于sleep的小實驗部署工具腳本中的問題

可以看到,源碼實作中的邏輯與 strace 看到的内容完全對應上了。 

      基于以上的内容,就可以很容易發現或解釋我們實際使用中的腳本存在哪些問題(公司内容,略)。 

繼續閱讀