[單刷APUE系列]第九章——程式關係

山河永寂發表於2019-05-11

目錄

[單刷APUE系列]第一章——Unix基礎知識[1]
[單刷APUE系列]第一章——Unix基礎知識[2]
[單刷APUE系列]第二章——Unix標準及實現
[單刷APUE系列]第三章——檔案I/O
[單刷APUE系列]第四章——檔案和目錄[1]
[單刷APUE系列]第四章——檔案和目錄[2]
[單刷APUE系列]第五章——標準I/O庫
[單刷APUE系列]第六章——系統資料檔案和資訊
[單刷APUE系列]第七章——程式環境
[單刷APUE系列]第八章——程式控制[1]
[單刷APUE系列]第八章——程式控制[2]
[單刷APUE系列]第九章——程式關係
[單刷APUE系列]第十章——訊號[1]

程式組

原著這裡前面實際上還有兩節,但是筆者感覺並不是特別重要,只是Unix各個實現的登入,所以就直接從程式組開始講。
在使用man 2 intro的時候,Unix系統手冊上已經寫了很多關於系統的概念,其中就有程式組概念

Each active process is a member of a process group that is identified by a non-negative integer called the process group ID.  This is the process ID of the group leader.  This grouping permits the signaling of related processes (see termios(4)) and the job control mechanisms of csh(1).

啟用的程式都是程式組的醫院,程式組ID由程式組頂部程式的pid作為標識,程式組主要用於相關程式之間的訊號傳遞和作業控制。由於程式組ID實際上是一個pid,所以Unix系統給出了以下函式

pid_t getpgrp(void);
pid_t getpgid(pid_t pid);

getpgrp函式是返回當前程式的程式組ID,SUS標準還規定了getpgid用於返回指定pid的程式組ID,如果pid為0,則返回當前程式的程式組ID。
程式組實際上是一個虛概念,並不是物理劃分的組,任何一個程式都可以認為是一個程式組,程式自身就帶有一個屬性用於標識自身的程式組,當程式組ID和自身程式ID相等時,就是一個只有一個程式的程式組,當然,程式也可以呼叫setpgid函式加入一個程式組或者建立一個新程式組。

int setpgid(pid_t pid, pid_t pgid);

setpgid函式將pid程式的程式組ID設定為pgid,如果pid為0,則使用呼叫者的程式ID,如果pgid為0,則由pid指定的程式ID作為程式組ID。

會話

A session is a set of one or more process groups.  A session is created by a successful call to setsid(2), which causes the caller to become the only member of the only process group in the new session.

會話是一系列程式組的集合,使用setsid函式來建立一個會話,並且當前的程式會變成新會話中的唯一程式組的唯一成員。既然前面提到了setsid函式,我們就來看看手冊說明

pid_t setsid(void);

The setsid function creates a new session.  The calling process is the session leader of the new session, is the process group leader of a new process group and has no controlling terminal.  The calling process is the only process in either the session or the process group.
Upon successful completion, the setsid function returns the value of the process group ID of the new process group, which is the same as the process ID of the calling process.

和前面會話的講解基本差不多,就多了一點——沒有控制終端。在說明頁(man 2 intro)中,筆者並沒有找到有關會話ID的概念,但是確實存在會話唯一標示符,可以類比前面的內容,大膽猜測一下,會話ID實際上就等於會話首程式的程式ID,實際上,在BSD系統歷史上,也確實引入了這個概念

pid_t getsid(pid_t pid);

The session ID of the process identified by pid is returned by getsid().  If pid is zero, getsid() returns the session ID of the current process.

這個基本不用多說了。

控制終端

控制終端是什麼,手冊頁上是這麼描述的,A terminal that is associated with a session is known as the controlling terminal for that session and its members.,除了控制終端以外,會話和程式組還有其他特點

  1. 會話有控制終端,只要是能實現終端功能的裝置都行

  2. 和控制終端建立連結的會話首程式是控制程式

  3. 會話中的程式組可以分為前臺程式組、後臺程式組

  4. 存在控制終端則必定存在前臺程式組

從上面可以看到幾個新名詞,控制程式、前臺程式組和後臺程式組,控制程式這裡也直接拿手冊頁描述,A session leader with a controlling terminal is a controlling process.,前臺後臺程式組可能經常使用Unix系統的人比較熟悉,前臺程式組在SSH斷線後就會自動終止,而後臺程式組則不會終止。

tcgetpgrp、tcsetpgrp和tcgetsid函式

pid_t tcgetpgrp(int fildes);
int tcsetpgrp(int fildes, pid_t pgid_id);
pid_t tcgetsid(int fildes);

tcgetpgrp函式看函式名好像是獲得程式組ID,但是實際上是根據終端裝置的檔案描述符返回前臺程式組ID,tcsetpgrp則是當程式有一個控制終端的時候,將前臺程式組ID設定為pgid_id,這個值應當是同一會話的一個程式組ID,filedes則是該會話的控制終端。實際上非常好理解,程式組ID的控制應當是歸屬會話的啟動程式的,也就是第一個程式,而更改前臺程式組ID則應當限定為同一會話,將後臺程式組更改為前臺,這就是這兩個函式的作用。
最後一個tcgetsid函式則是獲得會話ID,實際上前面也講過了,這個就是第一個程式組的第一個程式的程式ID。

作業控制

shell環境下的作業控制各位應當是很熟悉了,作業控制就是在一個終端下進行的多工處理,所有的作業實際上都是shell的子程式,這很容易理解。任何情況下,終端都只有一個前臺任務,其他都是後臺任務,對於後臺任務的處理,基本上都是使用&讓其在後臺執行,或者說是更加普遍的screen命令,當然,這個不在討論範圍內,在前面的講解中,有一些關於終端產生訊號的部分,其實有三個特殊字元用於產生訊號

  1. Ctrl+C => SIGINT

  2. Ctrl+ => SIGQUIT

  3. Ctrl+Z => SIGTSTP

其中前兩個特殊字元已經講過,最後一個Ctrl+Z就是掛起訊號。

shell執行程式

在前面講過,shell執行程式是通過exec函式族來進行的,也就是說,所有的程式都是shell的子程式,經過前面的講解,應該知道程式至少有pid、ppid、pgid和sid四種屬性,對於經典的sh終端來說,由於sh不支援作業控制,也就是說,只有一個前臺程式組存在,在實際使用中經常使用到管道,管道實際上是一系列序列的任務集合,shell非常巧妙的逆序將其fork自身,然後反向執行,也就是說,由於第一個任務的結果是第二個任務的輸入,所以實際上fork的過程是shell fork最後一個任務,逆序的向下派生的。筆者在這裡可能介紹的不夠全面,可以看原著學習。

孤兒程式組

在前面介紹過一個程式如果沒有了父程式,則父程式變為init程式,這種叫做孤兒程式,那麼也可以類比到程式組,是否存在孤兒程式組。這節也是可看可不看。

小結

實際上在寫這章的時候筆者是很頭疼的,因為基本沒什麼乾貨,都是些概念性的東西,很多都是man 2 intro上面看看就完事了,特別是前面的終端登入,實際上由於原著已經很老了,很多實現基本都變了,所以很難寫出實踐和感想,筆者建議就只看看原著就行了。

相關文章