天天看點

UNIX環境進階程式設計(第9章 程序關系)

程序之間具有關系,每個程序都有一個父程序(初始的核心程序并無父程序,也可以說父程序是它自己)。當子程序終止時,父程序得到通知并能取得子程序的退出狀态。也能通過waitpid等待程序組中的任意一個程序終止。

1程序組

每個程序都屬于一個程序組。程序組是一個或多個程序的集合。每個程序組有一個唯一的程序組ID——類似于程序ID,是一個正整數。每個程序組都可以有一個組長程序,組長程序的辨別是其程序組ID等于其程序ID。隻要在某個程序組中有一個程序存在,則該程序組就存在,這與其組長程序是否終止無關。

1.1建立程序組或加入現有程序組

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

頭檔案 #include <unistd.h>
函數原型 int setpgid(pit_t pid, pid_t pgid);
參數

pid:程序ID

pgid:程序組ID

傳回 若成功傳回0,出錯傳回-1
功能

加入現有程序組(而建立程序組需要通過setsid函數)。

1)      将pid程序的程序組ID設定為pgid,即是将pid程序加入到程序組ID為pgid的程序組

2)      如果pid與pgid相等,則由pid指定的程序變為程序組組長

3)      如果pid為0,則使用調用者的程序ID

4)      如果pgid為0,則由pid指定的程序ID将用作程序組ID

1.2擷取程序組ID

頭檔案 #include <unistd.h>
函數原型 int getpgid(pit_t pid);
參數 pid:程序ID
傳回 若成功傳回程序的程序組ID,出錯傳回-1
功能 傳回pid程序的程序組ID,若pid為0,則傳回調用程序的程序組ID

2會話

   會話(session)是一個或多個程序組的集合。會話開始于使用者登入,終止于使用者退出,此期間所有的程序都屬于這個會話期。

頭檔案 #include <unistd.h>
函數原型 pid_t setsid(void);
參數 void
傳回 若成功傳回程序的程序組ID,出錯傳回-1
功能 建立一個新會話。

調用該函數的程序已經是一個程序組的組長程序,則該函數傳回出錯。否則,該函數就會建立一個新會話,結果将發生下面3件事:

(1)該程序變成新會話首程序。此時該程序是新會話中唯一的程序。

(2)該程序成為一個新程序組的組長程序。

(3)該程序沒有控制終端。

會話的定義:

在shell支援工作控制(job control)的前提下,多個程序組還可以構成一個會話 (session)。bash(Bourne-Again shell)支援工作控制,而sh(Bourne shell)并不支援。

會話是由其中的程序建立的,該程序叫做會話的上司程序(session leader)。會話上司程序的PID成為識别會話的SID(session ID)。會話中的每個程序組稱為一個作業(job)。會話可以有一個程序組成為會話的前台工作(foreground),而其他的程序組是背景工作(background)。每個會話可以連接配接一個控制終端(control terminal)。當控制終端有輸入輸出時,都傳遞給該會話的前台程序組。由終端産生的信号,比如CTRL+Z, CTRL+\,會傳遞到前台程序組。

會話的意義在于将多個工作囊括在一個終端,并取其中的一個工作作為前台,來直接接收該終端的輸入輸出以及終端信号。其他工作在背景運作。

   一個指令可以通過在末尾加上&方式讓它在背景運作:

$ping localhost > log &

會話的定義:

由于Linux是多使用者多任務的分時系統,是以必須要支援多個使用者同時使用一個作業系統。當一個使用者登入一次系統就形成一次會話。一個會話可包含多個程序組,但隻能有一個前台程序組。每個會話都有一個會話首領(leader),即建立會話的程序。sys_setsid()調用能建立一個會話。必須注意的是,隻有目前程序不是程序組的組長時,才能建立一個新的會話。調用setsid之後,該程序成為新會話的leader。

  一個會話可以有一個控制終端。這通常是登陸到其上的終端裝置(在終端登陸情況下)或僞終端裝置(在網絡登陸情況下)。建立與控制終端連接配接的會話首程序被稱為控制程序。一個會話中的幾個程序組可被分為一個前台程序組以及一個或多個背景程序組。是以一個會話中,應該包括控制程序(會話首程序),一個前台程序組和任意背景程序組。 

  一次登入形成一個會話

  一個會話可包含多個程序組,但隻能有一個前台程序組

會話的定義: 

調用setsid會建立新的會話(session),那session到底是什麼?

當你通過控制台登入時,你就建立了一個會話(session),一個會話就是一個控制終端(controllingterminal,就是你巧鍵盤看顯示器的那個“終端”)、一個控制程序組(controllingprocess group,一般是登入shell),一個前台程序組,再加上多個背景程序組(background process group),其中控制程序(controlling process)。

在shell中,你可以用"&"将一個指令在背景運作,或者你可以按Ctrl-Z,然後用bg指令将其放入背景,這時你可以用jobs指令看到背景運作的程序,這些背景程序就是背景程序組(background process group)

    當你logout的時候會發生什麼,此時controlling terminal會被關閉,這個session中所有程序都會收到SIGHUP和SIGTERM/SIGQUIT,對于這些信号的預設操作就是結束程序。

    作為一個daemon,你當然不希望使用者logout的時候就退出,解決方案有幾種,一種就是忽略上述所有信号,例如nohup程式幹的就是這個,但這樣做有個小問題,很多程式使用信号作為一種簡單的IPC機制,忽略這些信号會導緻這種IPC失效;另外一種方案就是讓程序從這個controlling terminal上脫離,setsid将會建立一個新的session,使目前程序成為新session的leader,并且不再關聯之前session的controlling terminal。

3程序關系

1)程序:PID=程序ID,PPID=父程序ID

2)僵屍程序

3)孤兒程序

4)程序組:PGID=程序組ID

5)前台程序組

6)背景程序組

7)孤兒程序組

8)會話:SID=會話ID

9)控制終端:TPGID=控制終端程序組ID

僵屍程序:子程序先于父程序終止,且父程序尚未對其進行善後處理(擷取終止子程序的有關資訊,釋放它仍占用的資源),則該子程序稱為僵屍程序。

孤兒程序:父程序已經終止的所有程序,稱為孤兒程序。它們的父程序都改變為init程序。

每個程序都屬于一個程序組,每個程序組都屬于一個會話。

程序組:一個或多個程序的集合。通常,它們與同一作業相關聯,可以接收來自同一終端的各種信号。

程序組組長程序:程序ID等于程序組ID。組長可以改變子程序的程序組ID,使其轉移到另一程序組。

會話:一個或多個程序組的集合。一個登陸shell發起的會話,一般由一個會話首程序、一個前台程序組、一個背景程序組組成。中斷與控制終端的聯系。

會話首程序:建立會話時,會話中的唯一程序,其PID=SID。它通常是一個登陸shell,也可以在成為孤兒程序後調用setsid()成為一個新會話。

前台(foreground)程序組:該程序組中的程序能夠向終端裝置進行讀、寫操作的程序組。

背景(background)程序組:一個會話中,除前台程序組、會話首程序以外的所有程序組。該程序組中的程序能夠向終端裝置寫,但是當試圖讀終端裝置時,将會收到SIGTTIN信号,并停止。登入shell可以根據設定在終端上發出一條消息通知使用者有程序欲求讀終端。

孤兒程序組(定義1):該組中的每個成員的父程序要麼是該組的一個成員,要麼不是該組所屬會話的成員

孤兒程序組(定義2):不是孤兒程序組的條件是,該組中有一個程序,其父程序屬于同一會話的另一個組中。

背景作業不能讀終端

當背景作業試圖讀終端時,終端驅動程式檢測到這種情況,會向背景作業發送一個特定信号SIGTTIN,該信号會暫時停止此背景作業。如果将此背景作業轉為前台作業運作,它就可以讀終端。

[root]# cat > temp.foo & 背景啟動

[1] 3300

[root]#                                鍵入Enter鍵

[1]+ Stopped                 cat > temp.foo

[root]# fg %1                    使1号作業稱為前台作業

cat > temp.foo

hello,world!                      輸入1行後,鍵入檔案結束符CTRL+D退出

 [root]# cat temp.foo

hello,world!

[root]#

背景作業可以寫終端

背景作業寫終端是一個可以允許或禁止的選項。通過stty tostop指令禁止。

[root]# cat temp.foo &     背景執行

[1] 3302

[root]# hello,world!          背景作業的輸出

[1]+ Done                    cat temp.foo

[root]# stty tostop            禁止背景作業輸出到控制終端

[root]# cat temp.foo &    在背景再試一次

[1] 3304

[root]#                                鍵入Enter鍵,發現作業已經停止

[1]+ Stopped                 cat temp.foo

[root]# fg %1                    在前台恢複已經停止的作業

cat temp.foo                     shell告知現在哪一個作業在前台

hello,world!                      該作業的輸出

[root]#

linux的bg和fg指令:

  • 中斷字元(CTRL+C)産生SIGINT
  • 退出字元(CTRL+\)産生SIGQUIT
  • 挂起字元(CTRL+Z)産生SIGTSTP

    目前台運作一個程式需要很長時間時,但又需要幹其他事情,可以用CTRL+Z,挂起該程

序。

[root]# top

top - 14:56:41 up 3 days, 19:49, 2 users,  load average: 2.00, 2.00, 1.92

Tasks: 65 total,   3 running,  62 sleeping,  0 stopped,   0 zombie

Cpu(s): 99.8%us, 0.2%sy,  0.0%ni,  0.0%id, 0.0%wa,  0.0%hi,  0.0%si, 0.0%st

Mem:  2059432k total,   188488k used,  1870944k free,    17840k buffers

Swap:  786424k total,        0k used,   786424k free,    94772k cached

 PID USER      PR  NI VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND                                                

 3161 root     25   0  3656  244  176 R 77.2  0.0 114:07.82 a.out                                                 

 3188 root     25   0  3656  244  176 R 77.2  0.0  88:19.14 a.out                                                  

1 root     15   0 10352  680 572 S  0.0  0.0  0:02.11 init  

[1]+ Stopped                 top       []中的數字為作業号

[root]# bg 1                            把程式排程到背景執行

[1]- top &  

[root]# jobs                            檢視正在運作的任務

[1]+ Stopped                 top

[root]# fg 1                            把程式調到前台運作

top

top - 15:00:19 up 3 days, 19:53, 2 users,  load average: 2.00, 2.00, 1.92

Tasks: 67 total,   3 running,  62 sleeping,  2 stopped,   0 zombie

Cpu(s): 99.8%us, 0.2%sy,  0.0%ni,  0.0%id, 0.0%wa,  0.0%hi,  0.0%si, 0.0%st

Mem:  2059432k total,   189000k used,  1870432k free,    17912k buffers

Swap:  786424k total,        0k used,   786424k free,    94772k cached

 PID USER      PR  NI VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND                                               

 3161 root     25   0  3656  244  176 R 77.9  0.0 116:55.23 a.out                                                 

 3188 root     25   0  3656  244  176 R 77.9  0.0  91:06.38 a.out                                                  

   1 root      15   0 10352 680  572 S  0.0 0.0   0:02.11 init                                                  

   2 root      RT  -5    0    0    0 S  0.0  0.0   0:00.10 migration/0                                           

   3 root      34  19    0    0    0 S  0.0  0.0   0:00.00 ksoftirqd/0                                           

   4 root      RT  -5    0    0    0 S  0.0  0.0   0:00.03 migration/1 

   如果有多個作業,可用bg%jobnumber或fg %jobnumber