進程關係

1.終端,控制檯,控制終端的概念
linux

1.1控制檯和控制檯shell

終端和控制檯都不是我的電腦的概念,而是多人共用的小型中型大型計算機上的概念。
編程

一臺主機,連不少終端,終端爲主機提供了人機接口,每一個人都經過終端使用主機的資源.。windows

終端有字符啞終端和圖形終端兩種.服務器

控制檯是另外一種人機接口,,不經過終端與主機相連,,而是經過顯示卡-顯示器和鍵盤接口網絡

別與主機相連, 這是人控制主機的第一人機接口。函數

回到個人計算機上,我的計算機只有控制檯,沒有終端. 固然願意的話,,能夠在串口上連一測試

兩臺字符啞終端.。可是linux偏要按POSIX標準把我的計算機當成小型機來用,那麼就在控制ui

臺上經過getty軟件虛擬了六個字符啞終端(或者叫控制檯終端tty1-tty6,數量能夠在加密

/etc/inittab裏本身調)和一個圖型終端。在虛擬圖形終端中又能夠經過軟件(如rxvt)再虛擬

無限多個虛擬字符啞終端pts/0....)。記住,這全是虛擬的,用起來同樣,但實際上並非。

因此在我的計算機上,只有一個實際的控制檯,沒有終端,全部終端都是在控制檯上用軟件模擬的。
要把我的計算機當主機再經過串口或網卡外連真正的物理終端也能夠,但因爲真正的物理終端

並不比我的計算機自己便宜,通常沒有人這麼作。

1.2切換控制檯

如同其餘UNIX類系統,Linux自己也是基於命令行的。試試「Ctrl」+「Alt」+「Fx」。這就是控制檯算是Linux的原本面目。至於使用方法,除了多出登陸註銷外,其它操做和咱們在linux圖形界面

(X—window)下的終端操做是同樣的,在X-Window出問題或不運行X-Window的時候,操做

主要在這裏完成。

  Linux在控制檯下提供了不止一個(字符啞)終端,支持多用戶同時登陸,包括在本機同時登陸。

控制檯「Alt」+「Fx」可以切換到第x個(字符啞)終端。若是須要從X-Window裏跳到第(字符啞)終端,

須要「Ctrl」+「Alt」+「Fx」。通常狀況下若是要從控制檯返回Xwindow可用「「Alt」+7」來返回到Xwind的

圖形界面。(Linux發行版提供7個虛擬屏幕,1~6號是控制檯終端((字符啞)終端),第7個

上面跑X-Window。)

1.3控制終端(/dev/tty)

這是個在應用程序中的一個概念,前臺進程有個控制終端,就對應這個。

不過它並不指任何物理意義上的終端,其實/dev/tty會映射到當前的設備(經過tty命令能夠看到),

好比你若是在控制檯界面下(即字符界面下)那麼dev/tty就是映射到dev/tty1-6之間的一個(取決於你當

前的控制檯號),可是若是你如今是在圖形界面(Xwindows),那麼你會發現如今的/dev/tty映射到

的是/dev/pts的僞終端上。好比你能夠輸入命令 #tty 那麼將顯示當前映射終端如:/dev/tty1或者/dev/pts/0等。

///////////////////////////////////////////////在第四條以後buchong

(1)一個會話能夠有一個控制終端,這一般是終端設備(在如今的我的電腦上,一般是虛擬的tty1-tty6,而後圖形界面虛擬的),或者僞終端設備,創建與控制終端鏈接的首進程叫作控制進程,不是前臺進程組

(2)一個會話中的進程組能夠被分紅前臺進程組(必須該會話有一個控制終端)和後臺進程組(除前臺進程組以外的都是後臺進程組)

(3)若是鍵入delte或者是ctrl+c則(中斷信號),ctrl+\(終端退出鍵)被送往前臺進程組,而且當終端接口檢測到調制解調器斷開了鏈接,則將掛斷信號發送到控制進程


咱們通常不須要擔憂控制終端,通常控制終端在登錄的時候本身進行創建

POSIX.1分配控制終端的機制

介紹,好比說在linux3.2.0中,若是在第一次調用open的時候,前提是沒有O_NOCITY的時候,就將此open的控制終端分配給此會話

這是從system V派生的系統

下面的命令是相同的

有的時候不論是否標準輸入,標準輸出是否重定向,程序都須要與控制終端進行交互(若是重定向了,標準輸入輸出就不必定指向的是控制終端,所以在這裏就須要萬無一失準確的打開控制終端)

2終端登錄

2.1終端登錄::::(這裏是終端登錄,並非咱們日常的我的電腦直接登錄,可是大致相同)

(1)系統管理者建立一個一般名爲/etc/ttys的文件(這個文件原本就有),其中,每一個終端設備有一行,每一行說

明設備名和傳到getty程序的參數,這些參數說明了終端的波特率等。

(2)當系統自舉時,內核建立進程 ID 1,也就是init進程。init進程使系統進入多用戶狀態。init

讀文件/etc/ttys,對每個容許登陸的終端設備, init調用一次fork(以空環境執行getty程序),它所生成的子進程則

執行程序getty。        

(3) getty對終端設備調用open函數,以讀、寫方式將終端打開。若是設備是調制解調器,則open可能會在設備

驅動程序中滯留,直到用戶撥號調制解調器,而且線路被接通。一旦設備被打開,則文件描述符 0、一、2就被

設置到該設備。而後getty輸出「login:」之類的信息,並等待用戶鍵入用戶名。若是終端支持多種速度,則

getty能夠測試特殊字符以便適當地更改終端速度 (波特率)(綜上來看getty主要是用來打開終端,分配文件標識

符,而且最後調用login登錄程序)

(4)當用戶鍵入了用戶名後,getty就完成了。而後它以相似於下列的方式調用login程序:

execle("/usr/bin/login", "login", "-p", username, (char *) 0, envp);

(在gettytab文件中可能會有一些選擇項使其調用其餘程序,但系統默認是login程序)。init以一個空環境調用getty

。getty以終端名(例如TERM=foo, 其中終端foo的類型取自gettytab文件-->綠色的都放在envp中)和在gettytab中

的環境字符串爲 login建立一個環境( envp參數)。-p標誌通知login保留傳給它的環境,也可將其餘環境字符串加

到該環境中,可是不要替換它。圖 9 - 2顯示了login剛被調用後這些進程的狀態。

(5)login 能處理多項工做。由於它獲得了用戶名,因此能調用 getpwnam 取得相應用戶的口令文件登陸項。而後

調用 getpass(3)以顯示提示「 Password:」接着讀用戶鍵入的口令(天然,禁止回送用戶鍵入的口令)。它調用 

crypt(3)將用戶鍵入的口令加密,並與該用戶口令文件中登陸項的pw_passwd字段相比較。若是用戶幾回鍵入

口令都無效,則 login 以參數1調用 exit 表示登陸過程失敗。父進程(init)瞭解到子進程的終止狀況後,將再次

調用fork其後又跟隨着執行getty,對此終端重複上述過程。若是用戶正確登陸,login就將當前工做目錄更改

爲該用戶的起始目錄 (chdir)。它也調用chown改變該終端的全部權,使該用戶成爲全部者和組全部者。將對該

終端設備的存取許可權改變成:用戶讀、寫和組寫。調用setgid及initgroups設置進程的組ID。而後用login所得

到的全部信息初始化環境:起始目錄 (HOME )、shell ( SHELL)、用戶名(USER和LOGNAME),以及一

個系統默認路徑 (PATH)。最後login進程改變爲登陸用戶的用戶 I D (setuid )並調用該用戶的登錄shell,其方式相似於:execl("/bin/sh", "-sh", (char *) 0);(login主要是用來在getty獲得用戶名以後,進行登錄

argv[0]的第一個字符-是一個標誌,表示該shell被調用爲登陸shell.shell能夠查看此字符,並相應地修改其起動過程。

最後一步setuid改變一下3個用戶ID:實際用戶ID,有效用戶ID,和保存的用戶ID!!(登錄成功sh用來啓動一個shell,叫作登錄shell

3網絡登錄

3.1網絡登錄與終端登錄的區別

終端登陸中 ,init知道哪些終端設備可用來進行登陸,併爲每一個設備生成一個getty進程。可是,對網絡登陸則狀況

有所不一樣,全部登陸都經由內核的網絡界面驅動程序(例如:以太網驅動程序),事先並不知道將會有多少這樣的登

錄。不是使一個進程等待每個可能的登陸,而是必須等待一個網絡鏈接請求的到達。在4.3+BSD中,有一個稱

爲 i n e t d的進程(有時稱爲Internet superserver),它等待大多數網絡鏈接

3.2網絡登錄

(1)做爲系統起動的一部分,init調用一個shell,使其執行shell腳本etc/rc。由此shell腳本起動一個精靈進程 inted。一

旦此shell腳本終止,inted的父進程就變成init。inted等待TCP/IP鏈接請求到達主機,而當一個鏈接請求到達時,它

執行一次fork,而後該子進程執行適當的程序。(在這裏執行了一次fork)

(2)假定到達了一個對於TELNET服務器的TCP鏈接請求.TELNET是使用TCP協議的遠程登陸應用程序。在另

一個主機 (它經過某種形式的網絡,鏈接到服務器主機上 )上的用戶,或在同一個主機上的一個用戶起動 

TELNET客戶進程( c l i e n t )起動登陸過程:

                                                telnet hostname

該客戶進程打開一個到名爲hostname的主機的TCP鏈接,在hostname主機上起動的程序被稱爲TELNET服務器

。而後,客戶進程和服務器進程之間使用TELNET應用協議經過TCP鏈接交換數據。所發生的是起動客戶進程的

用戶如今登陸到了服務器進程所在的主機。(天然,用戶須要在服務器進程主機上有一個有效的帳號)。

圖 9 - 4顯示了在執行 TELNET服務器進程 (稱爲telnetd )中所涉及的進程序列。而後telnetd進程打開一個

僞終端設備,並用fork生成一個子進程。父進程處理經過網絡鏈接的通訊,子進程則執行login程序。父、子

進程經過僞終端相鏈接。在調用exec以前,子進程使其文件描述符 0 , 1 , 2與僞終端相連。若是登陸正確

, login就執行9.2節中所述的一樣步驟 — 更改當前工做目錄爲起始目錄,設置登陸用戶的組ID和用戶ID,

以及登陸用戶的初始環境。而後login用exec將其自身替換爲登陸用戶的登陸shell。圖9 - 5顯示了到達這一點

時的進程安排。

須要理解的重點是:當經過終端(見圖 9 - 3)或網絡(見圖 9 - 5)登陸時,咱們獲得一個登錄shell,其標準輸入、輸出

和標準出錯鏈接到一個終端設備或者僞終端設備上。在下一節中咱們會了解到這一登陸shell是一個PISXO.1對

話期的開始,而此終端或僞終端則是會話期的控制終端。


4進程組

每個進程都有一個進程組ID,進程組是一個或多個進程的集合,每個進程組都有一個進程組長,進程組組長的pid等於組id,進程組組長能夠建立一個進程組,只要本進程組有一個進程存在,這個進程組就是存在的

4.1取得進程組ID

#include<unistd.h>

pid_t getpgrp(void);

獲得調用進程的進程組ID,若是成功則返回進程組ID,出錯,返回-1

pid_t getpgid(pid_t pid);

獲得進程爲pid的進程組ID,若是pid是0,則獲得調用進程的進程組id

4.2設置進程組ID

#include <unistd.h>

int setpgid(pid_t pid,pid_t pgid);將pid的進程組ID設置成pgid

(1)若是pid不等於pgid:將指定pid的進程組的組ID設置成pgid

(2)pid=pgid:由pid指定的進程變成組長進程

(3)pid=0:使用調用者的pid,將其進程組的id設置成pgid

(4)pgid=0:則pid指定的進程ID,變成組長進程

一個進程只能給本身或者子進程設置組ID,而且在子進程調用了exec以後,就不能再進行進程組的設置

5會話

會話的定義,就是一個或者多個進程組

5.1建立一個新的會話

pid_t setsid(void);

建立一個新的會話

注意點

(1)應該保證調用此函數的進程不是進程組的組長,爲了保證不是進程組長ID,一般進行fork,而後終止其父進程,由於進程組是繼承的,因此不可能與新的ID重合

(2)該進程編程新會話的會話首進程

(3)該進程的ID成爲第一個進程組的ID,即新進程組的ID是該進程ID,第一個進程的ID也是會話ID

(4)該進程事沒有控制終端的,若是有,也進行切斷

pid_t tcgetpgrp(int fd);

函數返回前臺進程組的ID,這個進程組ID與fd相關連

int tcsetpgrp(int fd,pid_t pgrpid);

從fd找到前臺進程組ID,將前臺進程組ID設置成pgrpid,就這樣!!

pid_t getsid(void);

獲得會話首進程的進程組ID!!!

一個會話能夠沒有控制終端,沒有控制終端證實着沒有與終端進行交互的功能

pid_t tcgetsid(int fd);

給出控制TTY的文件描述符,經過此函數就能獲得會話首進程的進程組ID

6做業控制

有做業控制特性能夠在終端上連續啓動不少個做業(多個進程組)可是要具有做業控制必須

(1)支持做業控制的shell

(2)內核中的終端驅動程序必須支持做業控制(這體現子在當用戶輸入一個退出,中斷的時候,驅動程序應該接受)

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

列如在setsid的時候已經創建了會話,在登錄shell指向終端驅動的時候,其實在這裏創建了前臺進程組(即肯定了前臺進程組ID),在做業控制的時候,後臺進程組寫終端的時候會產生SIGTTOU信號,而終端產生的信號應該送往前臺進程組,setpgid是設置進程組ID,這樣就能夠把那些做業設置成前臺進程組,完美的解決了!!!!這是在有做業控制的時候的一些過程

4:shell執行程序

在沒有做業控制的shell上執行

則不論是前臺進程仍是後臺進程,進程組ID都是不變的,下面是後臺進程組,發現並無變化,進程組ID

若是,在後臺進程組中試圖讀控制終端(再有做業控制的時候,會產生開心好sigtin),在沒有做業控制的時候,沒有重定向標準輸入,則將標準輸入重定向到dev/null

若是經過打開/dev/tty的時候,則狀況發生改變,好比說

crypt < salaries | lpr&

這個命令,crypt是經過打開tty進行的讀寫,這樣子是無效的

這張圖解釋了爲何會出現這種狀況,如上圖,由於當執行ps的時候,其實cat1,cat2並無exec好 因此都是sh

5孤兒進程組

問題的提出:考慮一個進程,當他fork了一個子進程的時候,子進程中止,而後父進程終止,子進程應該如何繼續,子進程是否知道了他目前是一個孤兒進程?

5.1定義:該組中的每一個成員要麼是該組的一個成員,要麼不是該組所屬會話的成員

5.2解釋如圖所示的程序編程了孤兒進程組

當進行fork的時候,首先讓父進程進行睡眠,而後等待子進程運行,子進程中止本身,父進程因爲睡眠時間已到,因此exit進行退出,此時父進程退出發出sighup信號,子進程收到以後進行pr_ids信息打印,此時因爲父進程已死,因此子進程的父進程編程init,此時子進程所屬的進程組變成一個孤兒進程組,因爲向標準輸入讀一個數據,此時本進程變成了一個後臺進程,後臺進程在進程組是孤兒進程租的時候讀標準輸入,此時read應該返回一個errno,而且進行打印



相關文章
相關標籤/搜索