《unix環境高級編程》-09、進程關係-讀書筆記

一、基本概念

 終端登錄:過去30年,登錄過程沒有多少改變。

(1)系統管理員創建通常名爲/etc/ttys的文件,其中,每個終端設備都有一行,每一行說明設備名和傳遞給getty程序的參數。

(2)當系統自舉時,內核創建進程ID爲1的進程,也就是init進程。init進程使系統進入多用戶狀態。init進程讀文件/etc/ttys,每一個允許登錄的終端設備,init調用一次fork,它所生成的子進程則執行(exec)getty程序。   如下圖:各個進程的實際用戶ID和有效用戶ID都是0(即它們都具有超級用戶特權)。init以空環境執行getty程序。問:什麼叫空環境?

             進程ID1

                init

          /        |       \

                fork               每個終端調用一次fork

                  |

                init

                  |

                exec            每個子進程執行(exec)getty

                  |

               getty

(3)getty爲終端設備調用open函數,以讀、寫方式將終端打開。一旦設備被打開,文件描述符0、1、2就被設置到該設備。然後getty輸出”login:“之類的信息,並等待用戶鍵入用戶名

          進程ID1

             init                                 讀取/etc/ttys;

        /      |       \                           meige終端調用一次fork;創建空環境

            fork

              | 

            init

              |    exec

           getty                           打開終端設備(文件描述符0、1、2);讀用戶名;初始環境設置

             |     exec

          login

注:當用戶鍵入用戶名後,getty的工作就完成了,然後就類似於下面的方式調用login程序:

                         execl("/bin/login", "login", "-p", username, (char *)0, envp)

(4)login可以執行多項工作。因爲它得到了用戶名,所以能調用getpwnam取得相應用戶的口令文件登錄項。然後調用getpass(3)以顯示提示”password:”,接着讀用戶鍵入的口令。塔吊用crypt(3)將用戶鍵入的口令加密,並與該用戶在陰影口令文件登錄項的pw_passwd字段相比較。如果用戶幾次鍵入的口令都無效,則login以參數1調用exit表示登錄過程失敗。父進程(init)連接到子進程的中止情況後,將再次調用fork,其後接着執行getty,對此終端重複上述過程。


(5)如果用戶正確登錄,login就將執行如下工作:

--將當前工作目錄更改爲該用戶的起始目錄(chdir)

--調用chown改變該終端的所有權,使登錄用戶成爲他的所有者

--將對該終端設備的訪問權限改變成用戶讀和寫

--調用setgid以及initgroups設置進程的組ID

--用login所得到的所有信息初始化環境:起始目錄(HOME)、shell(SHELL)、用戶名(USER和LOGNAME)、以及一個系統默認路徑(PATH)。

--login進程改變爲登錄用戶的用戶ID(setuid)並調用該用戶的登錄shell:如下:

execl("/bin/sh", "-sh", (char *)0);


(6)到此爲止,登錄用戶的登錄shell開始運行。其父進程init進程ID(進程ID爲1),所以當此登錄shell終止時,init會得到通知(SIGCHLD信號),它會對該終端重複全部上述過程。將登陸shell的文件描述符0、1和2設置爲終端設備。


(7)現在,登錄shell讀取其啓動文件:

Bourne shell和Korn shell是:.profile。

GNU Bourne-again shell是: .bash_profile、.bash_login或者.profile

C shell是.cshrc和.login

這些啓動文件通常會改變某些環境變量,加上很多環境變量。


二、網絡登錄

1、通過串行終端登錄系統和通過網絡登錄系統的區別是:通過網絡登錄時,終端和計算機之間的連接不是點對點連接。


init知道哪些終端設備可用來進行登錄,併爲每個設備生成一個getty進程。但是,在網絡登錄情況下,所有登錄都經由內核的網絡接口驅動程序(如以太網驅動程序),事先並不知道將會有多少這樣的登錄)。


爲了使同一個軟件既能處理終端login、又能處理網絡login,系統使用了一個稱爲僞終端的軟件驅動程序,它仿真串行終端的運行行爲,並將終端操作映射爲網絡操作。


在BSD中,有一個稱爲inetd的進程,它等待大多數網絡連接。作爲系統啓動的一部分,init調用一個shell,使其執行shell腳本/etc/rc。由此shell腳本啓動一個守護進程inetd,一旦此shell腳本終止,inetd的父進程就變成init。inetd等待TCP/IP連接請求到達主機,而當一個連接請求到達時,它執行一次fork,然後聲稱的子進程執行適當的程序。


需要重點理解的是:

當通過終端或網絡登錄時,我們得到一個登錄shell,其標準輸入、輸出和標準出錯連接到一個終端設備或者僞終端設備上。




二、進程組

1、每個進程除了有一個進程ID之外,還屬於一個進程組。進程組是一個或多個進程的集合。通常,它們與同一個作業相關聯,可以接收來自同一終端的各種信號。每一個進程組有一個唯一的進程組ID。存放於pid_t數據結構中。

注:例如當再終端使用命令行格式啓動命令時,當終端退出時,這些進程 都會退出,因爲這些進程與此控制終端相連,則終端退出時,都可以接受來自此終端的退出信息,也就 共同退出了。

使用getpgrp可以返回調用進程的進程組ID。

#include  <unistd.h>

pid_t getpgrp(void);


2、每個進程組都可以有一個組長進程,組長進程的標識是,其進程組ID等於其進程組長ID

    組長進程可以創建一個進程組,創建該組中的進程,然後終止。只要在某個進程組中有一個進程存在,則該進程組就存在,這與其組長進程是否終止無關。從進程組創建開始到其中最後一個進程離開爲止的時間區間稱爲進程組的生存期。進程組中的最後一個進程可以終止,或者轉移到另一個進程組

進程可以通過setpgid來加入一個現有的組或者創建一個新進程組。

#include <unistd.h>

int  setpgid(pid_t pid,pid_t pgid)


注意:  

(1)一個進程只能爲它自己或者它的子進程設置進程組ID。在它的子進程調用了exec函數之一,它就不再能改變該子進程的進程組ID。


3、會話

 會話(session)是一個或多個進程組的集合。

一個進程組有一個唯一會話期首進程,會話期ID爲首進程ID。

會話期可以有一個單獨的控制終端,與控制終端連接的會話期首進程叫控制進程。


注1:

用戶用ssh登錄,則用戶的登錄shell就是該會話的會話期首進程,其PID就是會話PID。則在該shell上執行的任何前臺命令或者是後臺命令(&)都屬於該會話組。

當因網絡故障,終端接口都會檢測到ssh網絡連接斷開,向當前會話組包括前、後臺的所有進程發送SIGHUP信號導致這些進程都被終止。(即在終端上,那麼它們同屬於一個會話組,會話組ID就是登陸shell的進程ID)



進程調用  setsid  函數創建一個新會話。

#include <unistd.h>

pid_t  setsid(void);

如果調用此函數的進程不是一個進程組的組長,則此函數就會創建一個新會話,結果將發生以下三件事:

(1)該進程會變成新會話首進程(session leader)。會話首進程就是創建該會話的進程。此時,該進程是新會話中唯一的進程。

(2)該進程稱爲一個新進程組的組長進程。新進程組ID是該調用進程的進程ID。

(3)該進程沒有控制終端。如果在調用setsid之前該進程有一個控制終端,則這種聯繫也會被切斷。這段話是說,因爲啓動進程總是在某個終端通過命令行形式啓動,因此進程會具有控制終端,當使用fork之後,父進程退出,子進程調用setsid之後,就切斷了與控制終端之間的聯繫,當控制終端終止時,也就不會影響到子進程的運行。

注1:

當一個新用戶登錄到某臺機器時,登錄進程會創建一個新的會話,會話包含一個單獨的進程:用戶的登錄shell。登錄shell的功能是作爲會話的領導者(leader)。會話領導者的pid被用作會話ID。會話通常是一個或者多個進程組的集合。會話安排已登錄用戶的動作,並關聯用戶到一個控制終端上,控制終端是一個特定的tty設備,處理用戶的終端I/O。因此,會話主要是shell相關的。實際上沒有其它東西關心會話。


注2:

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


4、會話首進程是具有唯一進程ID的單個進程,所以可以將會話首進程的進程ID視爲會話ID。

會話首進程總是一個進程組的組長進程,所以兩者是等價的。


5、控制終端

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

(1)一個會話可以有一個控制終端。

(2)建立和控制終端連接的會話首進程被稱爲控制進程。

(3)一個會話中的幾個進程組可被分爲一個前臺進程組以及一個或者多個後臺進程組

(4)如果一個會話有一個控制終端,則它有一個前臺進程組,會話中的其他進程組則爲後臺進程組

(5)無論何時鍵入終端的中斷鍵(常常是DELETE或Ctrl+C),就會將中斷信號發送給前臺進程組的所有進程

(6)無論何時鍵入終端的退出鍵(常常是Ctrl+\),就會將退出信號發送給前臺進程組中的所有進程

(7)如果終端接口檢測到調制解調器已經斷開連接,則將掛斷信號發送給控制進程(會話首進程)。



三、作業控制

    作業控制: 它允許一個在終端上啓動多個作業(進程組),它控制哪一個作業可以訪問該終端,以及哪些作業在後臺運行。作業控制要求下面三種形式的支持:

(1)支持作業的shell

(2)內核中的終端驅動程序必須支持作業控制

(3)內核必須提供對某些作業控制信號的支持
















發佈了130 篇原創文章 · 獲贊 40 · 訪問量 82萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章