每一個進程都有一個pid惟一的表示它,雖然是惟一的,可是是能夠複用的。c++
ID爲0的進程一般是調度進程,因爲不執行任何磁盤上的程序,因此又是系統進程。網絡
ID爲1的進程一般是init進程,在自舉過程結束時由內核調用。異步
ID爲2的是頁守護進程,此進程負責支持虛擬存儲器系統的分頁操做。函數
pid_t getpid()
獲取調用進程的pid
pid_t getppid()
獲取調用進程的父進程pid
uid_t getuid()
獲取調用進程的用戶id
gid_t getgid()
獲取調用進程的組用戶id
複製代碼
可使用一些函數建立一個進程ui
子進程和父進程會繼續執行fork以後的命令,子進程得到父進程數據空間,堆棧的副本,可是不是共享的,父子進程共享的只有正文段spa
子進程還會複製父進程中標準io緩衝區中未清楚的部分命令行
注意因爲文件部分的實現,文件描述符指向的文件表項是由內核管理,因此父子進程是會指向一樣的文件表項的,除非修改文件描述符,文件表項中包含了文件的偏移量(即任意進程修改事後下一個進程讀到的就是修改的內容)!指針
網絡服務進程中通常會各自關閉它們不須要的文件描述符,以避免產生沒必要要的影響。code
如今的不少實現並不執行一個父進程數據段,堆棧的徹底副本,使用了寫時複製的技術。繼承
這些區域由父子進程共享,內核將它們的訪問權限改變爲只讀。若是父進程或者子進程中的任意一個試圖修改這些區域,則內核只爲修改區域那一塊內存製做一塊副本,一般是虛擬內存中的一頁。
事實上,內核也爲子進程分配了一段頁表,可是這些頁表指向父進程,而且標記爲只讀,當子進程試圖寫的時候會發生寫保護的錯誤,錯誤處理方式就是複製出錯的內容進入頁表。
pid_t fork()
兩種不一樣的返回值
子進程返回0
父進程返回建立的子進程的pid
複製代碼
vfork不一樣關於fork,通常它是爲了調用exec才建立的,因此
vfork中:
在父進程的空間中運行,不會像fork同樣執行寫時複製!其實就是關閉了頁表的只讀變成可寫。
一旦子進程修改數據(除了vfork的返回值),進行函數調用,或者沒有exec或者exit就返回均可能帶來問題
保證子進程先運行,一旦子進程中調用了exec或者exit以後纔會調度父進程恢復運行。
注意exit()函數和_exit()函數的區別,exit()函數會沖洗緩衝區,因此父進程的緩衝區極可能會被清洗。
pid_t vfork()
複製代碼
孤兒進程:
對於父進程已經終結的進程,它們的父進程都會改變爲init進程
操做過程爲: 當一個進程終結時,內核會逐個檢查全部活動進程,以判斷它是不是正要終結的進程的子進程,若是是,則將其的父進程id 改成1
注意,孤兒進程並不會變爲殭屍進程,由於它終結的時候init進程一定會調用一個wait函數取得其終止狀態。
殭屍進程:
終止進程的父進程調用wait或者waitpid時,能夠獲得一些子進程的信息,包括進程id,終止狀態和CPU時間總量。內核會根據這些信息釋放終止進程的存儲區,關閉其全部打開的文件。
一個已經終止,可是父進程還沒有對其進行善後處理的進程稱爲殭屍進程。
//老子不收屍,兒子變殭屍
//孤兒不用怕,養父是老大
//生個孫子當龍子,哈哈哈
//簡單的例子,不須要等待孫子進程,孫子進程也不會變成殭屍進程
if(fork())
{
//進程1處理進程2結束
wait(NULL);
}
else
{
//進程2fork出進程3再終結
if(fork())
{
exit();
}
else
{
//成功得到養父進程init
}
}
複製代碼
當一個進程正常或異常終止的時候,內核就會向其父進程發送SIGCHLD信號,這是個異步事件,可能在父進程運行的任什麼時候候發生。
//返回終結的子進程id
pid_t wait(int *statloc)
若是子進程已經終止而且是個殭屍進程,則當即返回而且取得該子進程的狀態
不然wait阻塞直到任意一個子進程終結,因爲返回子進程的id,因此總會知道是哪一個子進程終結了
statloc:
NULL 不關心終止狀態
整形指針 將終止狀態填入地址指向的內存中
pid_t waitpid(pid_t pid,int *statloc,int options)
pid:
-1 等待任意子進程
>0 等待進程id與pid相等的子進程終結
0 等待組id等於調用進程組id的任一子進程
<-1 等待組id等於pid絕對值的任一子進程
statloc同上
options:
0 阻塞等待
或者下面的按或的結果
WCONTINUE
WNOHANG 不阻塞直接返回0(若是沒有子進程爲殭屍進程的話)
WUNTRACED
複製代碼
注意,除了父進程能夠等待子進程的結束,子進程也能夠等待父進程的結束
//子進程等待父進程的結束
while(getppid()!=1)
{
//非阻塞
}
while(getppid()!=1); //阻塞方式
複製代碼
當進程調用一種exec的時候,該進程執行的程序徹底替換成爲新程序,而新程序則從其main函數開始執行,由於調用exec並不建立新進程,先後的進程id未改變,exec只是用磁盤上的一個新進程替換了當前進程的正文段,數據段,堆段和棧段。
用exec能夠初始執行新的程序,exit函數和wait函數處理終止和等待終止。
exec
//exec(l,v)(p)
//l表示將參數傳入到函數參數中
//v表示將參數定義爲一個傳入到函數的參數中
//p表明須要自定義環境變量,若沒有p則繼承進程的環境變量
int system(const char *cmdstring)
//會調用fork,返回該進程的終止狀態
至關於在命令行中直接輸入cmdstring
複製代碼
注意須要有超級權限
int setuid(uid_t uid) //設置用戶id int setgid(gid_t gid) //設置用戶組id 複製代碼