天天看點

第八章 程序控制8.3函數fork8.4函數vfork8.5函數exit8.6函數wait和waitpid8.9競争的條件8.10函數exec8.11更改使用者ID 群組ID8.12解釋器檔案8.13函數system8.14程序會計8.16程序的排程8.17程序時間

内容提要:

本章介紹unix系統的程序控制,包括建立新的程序、執行程式、程序終止。另外還介紹了程序屬性的各種ID以及他們如何受到程序原語的影響。

8.2程序的标實

每一個程序都有一個非負整型表示的唯一程序ID,但是程序ID可以複用。

系統中的專用程序:

排程程序:ID為0,常被稱為交換程序,該程序是核心的一部分,并不執行磁盤上的任何程式,也被稱為系統程序。

init程序:ID為1,init通常讀取與系統相關的初始化檔案,init程序不會終止,是所有孤兒程序的父程序。

頁守護程序:ID為2 ,此程序負責支援虛拟存儲系統的分頁操作。

程序的其他标示符:

pid_t getpid(void);  //傳回值:調用程序的程序ID
pid_t getppid(void); //傳回值:調用程序的父程序ID
uid_t getuid(void);   //傳回值:調用程序的實際使用者ID
uid_t geteuid(void) ; //傳回值:調用程序的有效使用者ID
gid_t getgid(void ) ; //傳回值:調用程序的實際組ID
gid_t getegid(void) ; // //傳回值:調用程序的有效組ID 
           

8.3函數fork

//傳回值:子程序傳回0,父程序傳回子程序的ID 若出錯,傳回-1;fork被調用一次,傳回兩次;

pid_t fork(void)//建立一個子程序、

子程序和父程序繼續執行fork調用之後的指令。子程序是父程序的副本,例如,子程序獲得父程序的資料空間、堆、棧的副本。注意,這是子程序所擁有的副本。父程序和子程序并不共享這些存儲空間部分。父程序和子程序共享正文段(CPU執行的機器指令部分)。

子程序對變量所做的改變,并不影響父程序中變量的值

檔案的共享

在重定向父程序的标準輸出時,子程序的标準輸出也被重定向,實際上fork的一個特性是父程序的所有打開檔案描述符都被複制到子程序中,父程序和子程序每個相同的打開描述符共享一個檔案表項。

在fork之後處理檔案描述符有一下兩種情況:

1、父程序等待子程序完成。在這種情況下,父程序無需對其描述符做任何處理。當子程序終止後,它曾經進行過讀寫操作的任意共享檔案描述符的檔案偏移量已做出了相應的更新。

2、父程序和子程序各自執行不同的程式段。在這種情況下,在fork之後,父程序和子程序各自關閉他們不需要使用的檔案描述符,這樣就不會幹擾對方使用的檔案描述符(經常用于網絡服務程式)

子程序和父程序的差別:

1、fork的傳回值不同

2、程序ID不同

3、兩個程序的父程序ID不同

4、子程序不繼承父程序設定的檔案鎖

5、子程序的未處理鬧鐘會被清除

6、子程序的未處理信号集會被設定為空集。

8.4函數vfork

vfork函數的調用序列和傳回值與fork相同,也是用于建立一個新的程序

vfork和fork函數的差別:

1、vfork不将父程序的位址空間完全複制到子程序中(因為子程序會立即調用exec,于是也就不會引用該位址空間),不過在調用exec之前,它在父程序的空間中運作,但是如果子程序修改資料、調用函數、或者沒有調用exec都會帶來一些未知的結果。

2、vfork保證子程序先運作,在它調用exec或exit之後父程序才可能被排程運作。

8.5函數exit

1、調用exit函數,其操作包括調用各種終止程式,關閉所有标準I/O流等

2、調用_exit 或_Exit 函數其目的是為程序提供一種無需運作終止處理程式或信号處理程式而終止的方法,對标準I/O流是否進行沖洗取決于實作。

3、如果子程序正常終止,則父程序可以獲得子程序的退出狀态。

孤兒程序:父程序在子程序之前終止

對于所有的孤兒程序,他們的父程序都改變為init程序

子程序完全消失,父程序在最終準備好檢查子程序是否終止時無法擷取它的終止狀态。此時核心為每一個終止的子程序儲存了一定量的資訊,這些資訊包括(程序ID、該程序的終止狀态、該程序使用的CPU時間總量),核心可以釋放終止程序使用的所有存儲區,關閉其所有打開的檔案。

僵死程序:一個已經終止、但是其父程序尚未對其進行善後處理(擷取終止子程序的有關資訊、釋放它仍占有的資源)的程序

8.6函數wait和waitpid

調用wait和waitpid可能發生:

1、如果其所有子程序還在運作,則阻塞。

2、如果一個子程序已終止,正等待父程序擷取其終止态,則取得該子程序的終止狀态立即傳回。

3、如果沒有任何子程序,則立即傳回出錯。

//若成功,傳回程序ID;出錯傳回或-;
pid_t wait(int  * statloc);
如果statloc不是一個空指針,則終止程序的終止狀态就存放在它所指向的單元内。如果不關心終止狀态,則可将該參數指定為空指針

pid_t waitpid(pid_t pid, int *statloc , int options);
           

waitpid函數中的pid參數:

pid=-1 等待任一子程序,waitpid與wait等效

pid > 0 等待程序ID與pid相等的子程序

pid == 0 等待組ID等于調用程序組ID的任一子程序

pid < -1 等待組ID等于pid絕對值的任一子程序

options參數:

0、WCONTINUED、WNOHANG 、WUNTRACED

waitpid函數提供了wait函數沒有提供的功能:

1、waitpid可以等待一個特定的程序;

2、waitpid提供了一個wait的非阻塞版本

3、waitpid通過WUNTRACED和WCONTINUED選項支援作業控制。

8.9競争的條件

競争的條件:當多個程序企圖對共享資料進行某種處理,而最後的結果又取決于程序運作的順序,則發生了競争條件

一個程序希望等待其子程序終止,則必須調用wait函數中的一個,如果一個程序要等待其父程序終止可以使用一下的循環

while(getppid() != 1)

sleep(1);//這種循環稱為輪詢。

8.10函數exec

當fork函數建立了新的子程序後,子程序往往調用一種exec函數以執行另一個程式。當調用一種exec函數時,該程序執行的程式完全被新程式替換,該新程式從新程式的main函數開始執行。

調用exec并不建立新的程序,是以前後程序ID并不改變,exec隻是用磁盤上的一個新程式替換了目前程序的正文段、資料段、堆段、棧段。

int execl(const char *path, const char *arg, ...);  
int execlp(const char *file, const char *arg, ...);  
int execle(const char *path, const char *arg,  
                ..., char * const envp[]);  
int execv(const char *path, char *const argv[]);  
int execvp(const char *file, char *const argv[]);  
int execve(const char *file, char *const argv[], char *const envp[]);  
           

//系統調用

int fexecve(int fd ,char const argv [], char const envp []);

參數說明:path代表可執行檔案的路徑名;file代表檔案名(預設在環境變量表中尋找可執行檔案,也可以指定具體的路徑名);fd表示檔案描述符; envp[ ]代表環境變量表,arg和argv[]代表要傳遞的指令行參數以(char*)0 和NULL結尾。

參數巧記:p代表取file檔案名做參數;f表示取檔案描述符;l取參數表;v表示取argv[ ]; e表示envp[ ];

8.11更改使用者ID 群組ID

組ID:組ID就是建立該檔案的使用者ID所在的組

附加組ID:現在的作業系統中,往往一個使用者不僅僅在一個組中,可以允許存在還幾個組中,是以現在的作業系統中提供了附加的組ID,供檔案通路檢查。

詳情:http://blog.csdn.net/woods2001/article/details/4753718

在unix系統中,特權(更改目前的日期表示法)以及通路控制(能否讀寫一個特定的檔案)是基于使用者ID群組ID的。當程式需要增權重限允許通路資源或者降低特權阻止對某些資源的通路時,需要更換使用者ID群組ID。

設定實際使用者ID和有效使用者ID

成功傳回,出錯傳回-;
int setuid(uid_t uid);//設定實際使用者ID和有效使用者ID
int setgid(uid_t uid);//設定實際使用者I組D和有效使用者組ID
           

更改使用者ID的規則:

1、若程序擁有超級使用者權限,則setuid函數将實際使用者ID、有效使用者ID以及儲存的設定使用者ID設定為uid

2、若程序沒有超級使用者權限,但是uid等于實際使用者ID或儲存的設定使用者ID,則setuid隻将有效使用者ID設定為uid。不更改實際使用者ID和儲存的設定使用者ID。

3、如果上面條件都不滿足,則errno設定為EPERM,并傳回-1;

思考:有效使用者ID、實際使用者ID、儲存設定使用者ID的差別?

答案:http://www.2cto.com/os/201304/203706.html

關于核心維護的3個使用者ID?

1、隻有超級使用者程序可以更改實際使用者ID。通常實際使用者ID是在使用者登入時,由login程式設定,而且不會不會改變。隻有超級使用者才有權改變。

2、僅當對程式檔案設定了使用者ID位時,exec函數才設定有效使用者ID,任何時候都可以調用setuid,将有效使用者ID設定為實際使用者ID或儲存的設定使用者ID。

3、儲存的設定使用者ID是由exec複制有效使用者ID得到。

注意:getuid和geteuid函數隻能獲得實際使用者ID和有效使用者ID的目前值。我們沒有可移植的方法獲得儲存的設定使用者ID的目前值。

//成功傳回0,出錯傳回-1

功能:交換實際使用者ID和有效使用者ID的值。

int setreuid(uid_t ruid, uid_t euid);

int setregid(gid_t rgid, gid_t egid);

8.12解釋器檔案

解釋器檔案,這種檔案是文本檔案,起始行的形式是:

# ! pathname

! 和pathname之間的空格是可選的,pathname通常是絕對路徑名,對于這種檔案的識别是由核心作為exec系統調用處理的一部分來完成的。

核心使用調用exec函數的程序實際執行的并不是該解釋器檔案,而是在該解釋器檔案第一行中pathname所指定的檔案。而且該解釋器檔案一定要具有可執行的權限。

第八章 程式控制8.3函數fork8.4函數vfork8.5函數exit8.6函數wait和waitpid8.9競争的條件8.10函數exec8.11更改使用者ID 群組ID8.12解釋器檔案8.13函數system8.14程式會計8.16程式的排程8.17程式時間

解釋器問件和解釋器的差別:

解釋器檔案:文本檔案,它以# !開頭。

解釋器:有該解釋器檔案第一行中的pathname指定。

8.13函數system

int system(const char *cmdstring)

system函數在其中實作中調用fork、execl、waitpid 函數;使用system函數而不是直接使用fork、execl、waitpid 函數的有點是:

system進行所需的各種出錯和信号處理。

設定使用者ID或者設定組ID程式決不能調用system函數:這樣會把有效使用者ID設定為0(root使用者),這種程式就可以通路root才能通路的資源。

8.14程序會計

1、每當程序結束時核心就會寫一個會計記錄,該會計記錄一般包括指令名、所使用的CPU時間總量、使用者ID、組ID、啟動時間等。

2、會計記錄所需要的各個資料都會由核心儲存在程序表中,并在一個新的程序被建立時初始化,程序終止時寫一個會計記錄。這将導緻兩個結果:

a、我們不能擷取永遠不終止的程序的會計記錄。如init程序和核心守護程序,

b、在會計檔案中記錄的順序對應于程序終止的順序,而不是它啟動的順序。

會計記錄對應于程序而不是程式,在fork之後,核心會為子程序初始化一個記錄,而不再一個新的程式執行時初始化,如調用exec函數并不會建立一個新的會計記錄,但是會改變相應記錄中的指令名。

char * getlogin(void);//擷取使用者登入時使用的名字

如果調用此函數的程序沒有連接配接到使用者登入時所使用的終端,則函數會失敗,傳回NULL;通常這類程序稱為守護程序。

8.16程序的排程

unix系統中,排程的政策和排程的優先級是由核心确定,程序可以通過調整nice值選擇以更低優先級運作,隻有特權程序允許提高排程權限。

函數

//成功傳回新的nice值,出錯傳回-1

int nice(int incr)//通過nice函數擷取或者更改它的nice值

incr參數被增加到調用程序的nice值上

//成功傳回nice值,若出錯傳回-1

getpriority(int which, id_t who)//擷取程序的nice值或者一組相關程序的nice值

which可以選擇三個函數:PRIO_PROCESS表示程序 PRIO_PGRP表示程序組, PRIO_USER表示使用者ID

//成功傳回0,出錯傳回-1;
int setpriority(int which, id_t who, int value); //可用于為程序、程序組、屬于特定使用者ID的所有程序設定優先級。value增加到NZERO上變為新的nice值。
           

8.17程序時間

3個時間:牆上時鐘時間、使用者CPU時間、系統CPU時間,任何程序可以通過調用times函數獲得它自己以及終止子程序的3個時間值。

//成功傳回流逝的牆上時鐘時間(以時鐘的滴答數為機關),出錯傳回-1;
clock_t times(struct tms * buf);
           

注:所有由此函數傳回的clock_t 值都用_SC_CLK_TCK(由sysconf函數傳回每秒時鐘的滴答數)轉換成秒數。

時鐘時間(牆上時鐘時間wall clock time):從程序從開始運作到結束,時鐘走過的時間,這其中包含了程序在阻塞和等待狀态的時間。

使用者CPU時間:就是使用者的程序獲得了CPU資源以後,在使用者态執行的時間。

系統CPU時間:使用者程序獲得了CPU資源以後,在核心态的執行時間。

程序的三種狀态為阻塞、就緒、運作。

時鐘時間 = 阻塞時間 + 就緒時間 +運作時間

使用者CPU時間 = 運作狀态下使用者空間的時間

系統CPU時間 = 運作狀态下系統空間的時間。

使用者CPU時間+系統CPU時間=運作時間。

來源: http://blog.chinaunix.net/uid-27629574-id-3880991.html

繼續閱讀