UNIX進程組,會話和作業控制

來源:互聯網
上載者:User

 

1. 進程組、會話與終端

(1).每個進程都屬於一個進程組。進程組是一個或多個進程的集合,通常它們與一組作業相關聯,可以接受來自同一終端的各種訊號。每個進程組都有唯一的進程組ID(整數,也可以存放在pid_t類型中)。

    #include <unistd.h>

    pid_t getpgrp(void);

        //傳回值;調用進程的進程組ID

    每個進程組都有一個組長進程,組長進程的標識是進程組ID等於其進程ID。組長進程可以建立一個進程組、建立該組中的進程。只有某個進程中有一個進程存在,則該進程就存在,與組長進程是否終止無關。從進程組建立開始到其中最後一個進程離開為止的時間區間成為進程組的生存期。進程組中最後一個進程可以終止或者轉移到另一個進程組中。

    進程調用setpgid(setsid也可以)可以參加一個現存的組或者建立一個新進程組

    #include <sys/types.h>

    #include <unistd.h>

    int setpgid(pid_t pid, pid_t pgid);

        //返回:若成功則為0,出錯為-1

    這將pid進程的進程組ID設定為pgid。如果pid是0,則使用調用者的進程ID。另外,如果pgid是0,則由pid指定的進程ID被用作為進程組ID。如果這兩個參數相等,則由pid指定的進程變成進程組組長。

    一個進程只能為它自己或它的子進程設定進程組I D。在它的子進程調用了exec後,它就不再能改變該子進程的進程組I D。

    在大多數作業控制shell中,在fork之後調用此函數,使父進程設定其子進程的進程組ID,然後使子進程設定其自己的進程組ID。這些調用中有一個是冗餘的,但這樣做可以保證父、子進程在進一步操作之前,子進程都進入了該進程組。否則依賴於哪一個進程先執行,就產生一個競態條件。

(2).session是一個或多個進程組的集合。

 

    例如,在shell中:

    $proc1 | proc2 &

    $proc3 | proc4 

那麼此時,session中就會有三個進程組存在,分別是{登陸shell(session leader)},{proc1, proc2}, {proc3, proc4}。   

    進程調用setsid函數就可建立一個新對話期。

    #include <sys/types.h>

    #include <unistd.h>

    pid_t setsid(void);

    如果調用此函數的進程不是一個進程組的組長,則此函數建立一個新對話期,結果為:

(a) 此進程變成該新對話期的對話期首進程(session leader,對話期首進程是建立該對話期的進程)。此進程是該新對話期中的唯一進程。

(b) 此進程成為一個新進程組的組長進程。新進程組ID是此調用進程的進程ID。

(c) 此進程沒有控制終端。如果在調用setsid之前此進程有一個控制終端,那麼這種聯絡也被解除。

    如果此調用進程已經是一個進程組的組長,則此函數返回出錯。為了保證不處於這種情況,通常先調用fork,然後使其父進程終止,而子進程則繼續。因為子進程繼承了父進程的進程組ID,而其進程ID則是新分配的,兩者不可能相等,所以這就保證了子進程不是一個進程組的組長。

    對話期和進程組有一些其他特性:

    • 一個對話期可以有一個單獨的控制終端(controlling terminal)。這通常是我們在其上登入的終端裝置(終端登入情況)或偽終端裝置(網路登入情況)。

    • 建立與控制終端串連的對話期首進程,被稱之為控制進程(controlling process)。

    • 一個對話期中的幾個進程組可被分成一個前台進程組(foreground process group)以及一個或幾個後台進程組(background process group)。前台進程組接受終端輸入訊號。Shell中的作業控制就是對前後台進程組的控制,&或Ctrl+Z的進程組就是後台進程組。

    • 如果一個對話期有一個控制終端,則它有一個前台進程組,其他進程組則為後台進程組。

    • 無論何時鍵入中斷鍵(常常是DELETE或Ctrl-C)或退出鍵(常常是Ctrl-\),就會造成將中斷訊號或退出訊號送至前台進程組的所有進程。

    • 終端的掛斷訊號送至控制進程(對話期首進程。)

    • 系統在登陸時將自動建立控制終端。

    如何分配一個控制終端依賴於實現。在open時,有幾個和控制終端相關的選項:O_NOCTTY 如果要開啟的檔案為終端機裝置時,則不會將該終端當成進程式控制制終端。

    有時不管標準輸入、標準輸出是否重新定向,程式都要與控制終端互動作用。保證程式讀寫控制終端的方法是開啟檔案/dev/tty,在核心中,此特殊檔案代表控制終端。如果程式沒有控制終端,則開啟此裝置將失敗。

    注意:控制終端只有一個,通常控制終端/dev/tty代表當前shell的控制終端,其實是一個指向實際終端裝置的串連。實際的終端裝置可能是tty1,ttyS1或者pst/1.

(3).控制終端與終端

    首先介紹兩個抽象概念:

    tty(終端裝置的統稱):tty一詞源於Teletypes,或者teletypewriters,原來指的是電傳打字機,是通過串列線用印表機鍵盤通過閱讀和發送資訊的東西,後來這東西被鍵盤與顯示器取代,所以現在叫終端比較合適。終端是一種字元型裝置,它有多種類型,通常使用tty來簡稱各種類型的終端裝置。

    pty(偽終端,虛擬終端):遠程telnet到主機或使用xterm時不也需要一個終端互動。

    在Linux系統的裝置特殊檔案目錄/dev/下,終端特殊裝置檔案一般有以下幾種:

  1、序列埠終端(/dev/ttySn) 序列埠終端(Serial Port Terminal)是使用電腦序列埠串連的終端裝置。電腦把每個序列埠都看作是一個字元裝置。有段時間這些序列埠裝置通常被稱為終端裝置,因為那時它的最大用途就是用來串連終端。

   2、偽終端(/dev/pty/) 偽終端(Pseudo Terminal)是成對的邏輯終端裝置(即master和slave裝置, 對master的操作會反映到slave上)。例如/dev/ptyp3和/dev/ttyp3(或者在裝置檔案系統中分別是/dev/pty /m3和/dev/pty/s3)。它們與實際物理裝置並不直接相關。如果一個程式把ptyp3(master裝置)看作是一個序列埠裝置,則它對該連接埠的讀/ 寫操作會反映在該邏輯終端裝置對應的另一個ttyp3(slave裝置)上面。而ttyp3則是另一個程式用於讀寫操作的邏輯裝置。telnet主機A就是通過“偽終端”與主機A的登入程式進行通訊。

    3、控制終端(/dev/tty) 如果當前進程有控制終端(Controlling Terminal)的話,那麼/dev/tty就是當前進程的控制終端的裝置特殊檔案。可以使用命令”ps –ax”來查看進程與哪個控制終端相連。對於你登入的shell,/dev/tty是你當前的控制終端,裝置號是(5,0)。使用命令”tty”可以查看它具體對應哪個實際終端裝置。/dev/tty有些類似於到實際所使用終端裝置的一個聯結。在當前的控制終端的讀寫都會寫到當前的終端裝置中,例如echo "hello" > /dev/tty ,都會直接顯示在當前的終端中。而cat </dev/tty會從當前終端讀取輸入(行緩衝)並輸出出來。

    4、控制台終端(/dev/ttyn, /dev/console) 在Linux 系統中,電腦顯示器通常被稱為控制台終端(Console)。它模擬了類型為Linux的一種終端(TERM=Linux),並且有一些裝置特殊檔案與之相關聯:tty0、tty1、tty2 等。當你在控制台上登入時,使用的是tty1。使用Alt+[F1—F6]按鍵組合時,我們就可以切換到tty2、tty3等上面去。tty1–tty6等稱為虛擬終端,而tty0則是當前所使用虛擬終端的一個別名,系統所產生的資訊會發送到該終端上(這時也叫控制台終端)。因此不管當前正在使用哪個虛擬終端,系統資訊都會發送到控制台終端上。/dev/console即控制台,是與作業系統互動的裝置,系統將一些資訊直接輸出到控制台上。目前只有在單一使用者模式下,才允許使用者登入控制台。

    5、虛擬終端(/dev/pts/n)在Xwindows模式下的偽終端.如我在Kubuntu下用konsole,就是用的虛擬終端,用tty命令可看到/dev/pts/1。

    6、其它類型Linux系統中還針對很多不同的字元裝置存在有很多其它種類的終端裝置特殊檔案。例如針對ISDN裝置的/dev/ttyIn終端裝置等。

    

(4).幾個常用的終端相關命令

(a). 在ubuntu等發行版本中,圖形介面下Ctrl+Alt+F1-F6是開啟tty1-6的終端()。在tty1-6這些終端下Alt1-6是切換終端,Alt+F7進入圖形介面。

 

(b). 可以通過ps -t的方式查看其他終端進程(這些終端在初始進入時候屬於getty狀態,由於tty1終端尚未登入所以運行getty。而且沒有其他進程使用tty1終端):

    $ps -t tty1

    PID TTY          TIME CMD

    1524 tty1     00:00:00 getty

 

(c). 可以使用shell的tty命令來識別現在使用的終端:

    $ tty

    /dev/pts/0

 

(d). stty - set tty, change and print terminal line settings

$stty -a    命令用於檢查和修改當前控制終端的通訊參數。UNIX系統為鍵盤的輸入和終端的輸出提供了重要的控制手段,可以通過stty命令對特定終端或通訊線路設定選項.

$stty tostop        #[-]tostop  STOP嘗試向終端寫入資料的背景工作。(SIGTTOU)

$echo "hello world" &   #試圖輸出的進程會被終止

[1] 3063

$ fg

echo "hello world"

hello world

 

$ stty -tostop

$ echo "hello world" &

[1] 3065

hello world     #不STOP結果直接被輸出出來。

$ jobs

[1]+  完成                 echo "hello world"

所有選項,-option_name是關閉,option_name是開啟。對於控制終端的設定也是管理中重要的工作之一。

 

(4).需要有一種方法來通知核心哪一個進程組是前台進程組,這樣,終端裝置驅動程式就能瞭解將終端輸入和終端產生的訊號送到何處。

    #include <termios.h>

    #include <unistd.h>

    int tcgetattr(int fd, struct termios *termios_p);   //成功則返回與終端檔案描述符fd相關聯的前台進程的組ID,出錯則返回-1。

    int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);   //成功則返回0,出錯則返回-1

    //struct termios定義一和終端相關的識別欄位,例忽略BREAK鍵,忽略校正等等。

    大多數應用程式不直接調用這兩個函數,它們通常由作業控制shell調用。

 

 

2. 作業控制

(1).允許在一個終端上起動多個作業(進程組),控制哪一個作業可以存取該終端,以及哪些作業在後台運行。作業控制要求三種形式的支援:

(a).支援作業控制的shell。

(b).核心中的終端驅動程式必須支援作業控制。

(c).必須提供對某些作業控制訊號的支援。

三個特殊字元可使終端驅動程式產生訊號,並將它們送至前台進程組,它們是:

• 中斷字元(一般採用DELETE或Ctrl-C)產生SIGINT。

• 退出字元(一般採用Ctrl-\)產生SIGQUIT。

• 掛起字元(一般採用Ctrl-Z)產生SIGTSTP。

(2).不支援作業控制的Shell

    對於不支援作業控制的Shell,例如bsh,它的命令和它自身的進程處於同一個會話和前台進程組。在後台執行的命令(&)和管道命令的進程依然和Shell是同一個進程組。

    如果一個後台進程試圖取走終端,例如cat > temp &。在有作業控制時,後台作業被放在後台進程組中。如果後台作業試土讀控制終端,則會產生訊號SIGTTIN。在沒有作業控制時,其處理方法是如果該進程自己沒有重新導向標準輸入,則Shell會自動將標準輸入重新導向到/dev/null。讀/dev/null則會產生一個EOF讓cat讀到檔案末尾,正常結束。另外,管道執行的結構圖如下:

<圖>

(3).支援作業控制的Shell

$ ps -o pid -o ppid -o sid -o pgid -o command

  PID  PPID   SID  PGID COMMAND

 2074  2068  2074  2074 /bin/bash

 2580  2074  2074  2580 ps -o pid -o ppid -o sid -o pgid -o command

可以看出它們有不同的PGID。對於管道命令,他們屬於同一個進程組:

$ ps -o pid -o ppid -o sid -o pgid -o command | cat

  PID  PPID   SID  PGID COMMAND

 2074  2068  2074  2074 /bin/bash

 2584  2074  2074  2584 ps -o pid -o ppid -o sid -o pgid -o command

 2585  2074  2074  2584 cat

(4).SIGHUP訊號

    SIGHUP會在以下3種情況下被發送給相應的進程:

1、終端關閉時,該訊號被發送到session首進程以及作為job提交的進程(即用& 符號提交的進程)

2、session首進程退出時,該訊號被發送到該session中的前台進程組中的每一個進程

3、若父進程退出導致進程組成為孤兒進程組,且該進程組中有進程處於停止狀態(收到SIGTSTP訊號),SIGHUP會被發送到該進程組中的每一個進程。

    系統對SIGHUP訊號的預設處理是終止收到該訊號的進程。所以若程式中沒有捕捉該訊號,當收到該訊號時,進程就會退出。

    If a controlling process exits, the system revokes further access to the controlling terminal and sends a SIGHUP signal to the foreground process group. If a process such as a job-control shell exits, each process group that it created will become an orphaned process group。

(5).bash作業控制命令

(a). nohup:使用nohup讓程式永遠後台運行

   由於很多程式不是守護進程,我們又想讓它在後台運行,不受SIGHUP訊號影響(例如shell退出或者終端串連斷開),那麼使用nohup命令。

    $nohup sleep 100 &

    $appending output to nohup.out  #無論是否將命令重新導向輸出輸出終端,輸出都將附加到目前的目錄的nohup檔案中。

    $ps -t pts/0

    #可以看到sleep 100進程終端為pts/0

    $exit

   然後開啟另一終端:

   $ tty

    /dev/pts/1

    $ ps -ef | grep sleep | grep -v grep

    luffy     4171     1  0 18:12 ?        00:00:00 sleep 100

    #100秒後重新查詢,結果為空白。說明進程正常退出   

(b). 作業號%n:支援作業控制的Shell可以識別%+作業號的作業進程

例如:

$ sleep 10&

[1] 4294

$ %1        #bring %1 to front,same as fg %1

sleep 10

(c).$fg 則將第一個作業放到前台。fg %n

(d).bg 將一個在後台暫停命令,變成繼續執行 如果後台中有多個命令,可以用bg %jobnumber將選中的命令調出,%jobnumber是通過jobs命令查到的後台正在執行的命令的序號(不是pid)。

(e).jobs

(f). ctrl+z or &

 

(6).Linux讓進程在後台執行

(a). nohup命令:略

(b). setsid命令. 如果我們的進程不屬於接受HUP 訊號的終端的子進程,那麼自然也就不會受到HUP 訊號的影響了。setsid 就能協助我們做到這一點。系統調用setsid()請見前面。

$setsid ping www.baidu.com

$ PING www.a.shifen.com (119.75.218.70) 56(84) bytes of data.

64 bytes from 119.75.218.70: icmp_req=1 ttl=54 time=1.88 ms

......

^C

64 bytes from 119.75.218.70: icmp_req=3 ttl=54 time=1.65 ms

64 bytes from 119.75.218.70: icmp_req=4 ttl=54 time=1.92 ms

......

輸出資訊會不斷出現在這個終端,由於這個進程已經不是這個Shell的會話了,所以Ctrl+C不能終止這個進程。就算關閉整個終端,程式也會繼續執行。

在另一個終端將它終止 :

$ ps -e -o pid -o sid -o pgid -o command | grep ping | grep -v grep

 4474  4474  4474 ping www.baidu.com

#可以看出sid=pid.由於是

$ kill -SIGKILL 4396

$ ps -ef | grep ping |grep -v grep

進程被kill掉。

(c).(&)

subshell:一個或多個命令包含在()裡執行就能讓他們在子shell中執行。所以讓在子shell中執行的作業用jobs看不到,自然也不會接收任何hup訊號。例如:

$(ping www.baidu.com &)

(d).disown

對於已經提交出去的命令,使用作業調度disown來達到目的:

用disown -h jobspec 來使某個作業忽略HUP訊號。

用disown -ah 來使所有的作業都忽略HUP訊號。

用disown -rh 來使正在啟動並執行作業忽略HUP訊號。

# cp -r testLargeFile largeFile &

[1] 4825

# jobs

[1]+  Running                 cp -i -r testLargeFile largeFile &

# disown -h %1

# ps -ef |grep largeFile

root      4825   968  1 09:46 pts/4    00:00:00 cp -i -r testLargeFile largeFile

root      4853   968  0 09:46 pts/4    00:00:00 grep largeFile

# logout  

對於正在運行,沒有加&放到後台啟動並執行程式可以先ctrl+Z停止進程,然後用bg %n讓這個作業繼續執行。再用disown忽略hup訊號。

(e). screen終端模擬器,功能強大這裡只簡單介紹。

沒有啟動screen的進程樹 :

$ ping www.baidu.com >~/temp.output &

[1] 4962

$ pstree -H 4962        #讓指定的進程高亮顯示

init─┬─/usr/bin/termin─┬─bash

     │                ├─bash─┬─ping

......

在使用了screen的進程樹

# screen -r Urumchi

# ping www.ibm.com &

[1] 9488

# pstree -H 9488

init-+-/usr/bin/termin-+-bash

     |                 |-bash-+-ping

     |                 |      `-screen---screen-+-bash

 

Reference:

APUE

man

Stty使用一技    http://fanqiang.chinaunix.net/a1/b4/20020606/060200245.html

Linux中的終端、控制台、tty、pty等概念    http://news.newhua.com/news1program_language/2010/623/10623141048745773199BCF0CFH6AKB9930IGCFKHBH4IBE65IDFI07F.html

IBM文庫-Linux 技巧:讓進程在後台可靠啟動並執行幾種方法http://www.ibm.com/developerworks/cn/linux/l-cn-nohup/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.