標籤:
一、進程標識
每個進程都有一個非負整型表示的唯一進程ID。雖然該id是唯一的,但是進程ID是可複用的。當一個進程終止後,其進程id就成為複用的候選者。
系統中有一些專用進程,但是具體細節隨實現而不同。ID為0的進程通常是調度進程,常常被稱為交換進程。該進程是核心的一部分,它並不執行任何磁碟上的程式,因此也被成為系統進程。進程ID 1通常是init進程,在自舉過程結束時有核心調用。init進程絕不會終止,他是一個普通使用者的使用者進程(與交換進程不同,他不是核心中的系統進程),但是它以超級使用者權限運行。
每個unix系統實現都有它自己的一套提供作業系統服務的核心進程,比如,在某些unix的虛擬儲存空間中,進程id 2是頁守護進程(page daemnon),此進程負責支援虛擬儲存系統的分頁操作。
附:進程相關函數
1 #include <sys/types.h>2 #include <unistd.h>3 pid_t getpid(void); 返回:調用進程的進程 I D4 pid_t getppid(void); 返回:調用進程的父進程 I D5 uid_t getuid(void); 返回:調用進程的實際使用者 I D6 uid_t geteuid(void); 返回:調用進程的有效使用者 I D7 gid_t getgid(void); 返回:調用進程的實際組 I D8 gid_t getegid(void); 返回:調用進程的有效組 I D
二、fork函數
#include<unistd.h>pid_t fork(void);
解釋:有fork函數建立的新進程成為子進程,fork函數被調用一次,但返回兩次,兩次返回的區別是子進程傳回值是0,父進程的傳回值則是建立子進程的進程id。子進程和父進程繼續執行fork調用之後的指令,子進程是父進程的副本。例如,子進程擷取父進程資料空間、堆和棧的副本。注意,這是子進程所擁有的副本,父進程和子進程並不共用這些儲存空間部分。該部分執行寫時複製。
1 #include<sys/types.h> 2 #include "ourhdr.h" 3 int glob=6; 4 char buf[]="a write to stdout\n"; 5 int main(void) 6 { 7 8 int var; 9 pid_t pid;10 var=88;11 if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)12 printf("write error");13 printf("before fork\n");14 if((pid=fork())<0)15 printf("fork error\n");16 else if(pid==0)17 {18 glob++;19 var++;20 }21 else22 sleep(2);23 printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);24 exit(0);25 }
可以看到子進程對變數所做的改變並不影響父進程中該變數的值。
一般來講,在fork之後是父進程還是子進程先執行是不確定的,這取決於核心所使用的調度演算法,如果要求父進程和子進程之間相互同步,則要求某種形式的處理序間通訊。
檔案分享權限設定
在fork之後處理檔案描述符有以下兩種常見的情況:
(1)父進程等待子進程 完成。(2)父進程和子進程各自執行不同的程式段。
除了開啟檔案之外,父進程的很多其他屬性也有子進程繼承,包括:
實際使用者I D、實際組I D、有效使用者I D、有效組I D。
• 添加組I D。
• 進程組I D。
• 對話期I D。
• 控制終端。
• 設定-使用者- I D標誌和設定-組- I D標誌。
• 當前工作目錄。
• 根目錄。
• 檔案方式建立屏蔽字。
• 訊號屏蔽和排列。
• 對任一開啟檔案描述符的在執行時關閉標誌。
• 環境。
• 串連的共用儲存段。
• 資源限制。
父、子進程之間的區別是:
• fork的傳回值。
• 進程I D。
• 不同的父進程I D。
• 子進程的t m s _ u t i m e , t m s _ s t i m e , t m s _ c u t i m e以及t m s _ u s t i m e設定為0。
• 父進程設定的鎖,子進程不繼承。
• 子進程的未決警示被清除。
• 子進程的未決訊號集設定為空白集。
fork失敗的兩個原因:
(a)系統中已經有了太多的進程(通常意味著某個方面出了問題)
(b)該實際使用者id的進程總數超過了系統限制。
fork的用法:
(1)一個父進程希望賦值自己,是父進程和子進程同時執行不同的程式碼片段。
(2)一個進程要執行一個不同的程式。
函數vfork
(1)vfork函數用於建立一個新的進程,而該進程的目的是exec一個新程式。vfork與fork的區別:(1)v f o r k與f o r k一樣都建立一個子進程,但是它並不將父進程的地址空間完全複製到子進程中,因為子進程會立即調用 e x e c (或e x i t ),於是也就不會存訪該地址空間。不過在子進程調用 e x e c或e x i t之前,它在父進程的空間中運行。這種工作方式在某些 U N I X的頁式虛存實現中提高了效率(與上節中提及的,在 f o r k之後跟隨e x e c,並採用在寫時複製技術相類似)。
(2)v f o r k保證子進程先運行,在它調用 e x e c或e x i t之後父進程才可能被調度運行。(如果在調用這兩個函數之前子進程依賴於父進程的進一步動作,則會導致死結。)
函數exit
進程有5種正常終止和3種異常終止方式
5種正常終止方式為:
在main函數內執行return語句,調用exit函數,調用_exit函數或者_Exit函數,進程的最後一個線程在其啟動常式中執行return語句,進程的最後一個線程調用pthread_exit函數。
3種異常終止方式如下:
調用abort,當進程接收到某些訊號時,最後一個線程對“取消”請求做出響應。
在任意一種終止情況下,該終止進程的父進程都可以用wait或waitpid函數取得其終止狀態。
對於父進程已經終止的所有進程,它們的父進程都改變為init進程,我們稱這些進程由init進程收養。
僵死進程:一個已經終止、但是其父進程尚未對其進行善後處理的進程成為僵死進程。
函數wait和waitpid
#include<sys/wait.h>pid_t wait(int *statloc);pid_t waitpid(pid_t pid,int *statloc,int options);
調用wait和waitpid的進程會發生什麼
• 阻塞(如果其所有子進程都還在運行)。
• 帶子進程的終止狀態立即返回(如果一個子進程已終止,正等待父進程存取其終止狀態 )。
• 出錯立即返回(如果它沒有任何子進程)。
這兩個函數的區別是:
• 在一個子進程終止前, wait 使其調用者阻塞,而 waitpid 有一選擇項,可使調用者不阻
塞。
• waitpid並不等待第一個終止的子進程—它有若干個選擇項,可以控制它所等待的進程。
檢查w a i t和w a i t p i d所返回的終止狀態的宏
對於waitpid函數中pid參數的作用的解釋如下:
• pid == -1 等待任一子進程。於是在這一功能方面 w a i t p i d與w a i t等效。
• pid > 0 等待其進程I D與p i d相等的子進程。
• pid == 0 等待其組I D等於調用進程的組I D的任一子進程。
• pid < -1 等待其組I D等於p i d的絕對值的任一子進程。
對於waitpid函數中options參數的作用的解釋如下:
options或者是0或者是表中常量按位或者運算的結果。
waitpid函數提供了wait函數沒有提供的三個功能:
(1) waitpid等待一個特定的進程 (而wait則返回任一終止子進程的狀態 )。
(2) waitpid提供了一個wait的非阻塞版本。有時希望取得一個子進程的狀態,但不想阻塞。
(3) waitpid支援作業控制(以WUNTRACED選擇項)。
1 #include<sys/wait.h> 2 #include<unistd.h> 3 #include<stdio.h> 4 #include<stdlib.h> 5 int main(void) 6 { 7 pid_t pid; 8 if((pid=fork())<0) 9 {10 perror("fork");11 }else if(pid==0)12 {13 if((pid=fork())<0)14 perror("fork");15 else if(pid>0)16 exit(0);17 sleep(2);18 printf("second child,parent pid is:%d\n",(long)getppid());19 exit(0);20 }21 if(waitpid(pid,NULL,0)!=pid)22 perror("waitpid");23 exit(0);24 }25 ~
以上程式實現的功能是:一個進程fork一個子進程,但不要它等待子進程終止,也不希望子進程處於僵死狀態知道父進程終止,通過調用fork兩次來實現。
在第二個子進程中調用 sleep以保證在列印父進程 ID時第一個子進程已終止。在 fork之後,父、子進程都可繼續執行——我們無法預知哪一個會先執行。如果不使第二個子進程睡眠,則在fork之後,它可能比其父進程先執行,於是它列印的父進程 ID將是建立它的父進程,而不是init進程。
LINUX環境編程之進程式控制制(上)