unix編程以及xv6系統淺談(五)進程控制

5.1 進程標誌

​ 每一個進程都有一個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
複製代碼

5.2 建立進程

​ 可使用一些函數建立一個進程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()
複製代碼

5.3 殭屍進程和孤兒進程

孤兒進程

​ 對於父進程已經終結的進程,它們的父進程都會改變爲init進程

​ 操做過程爲: 當一個進程終結時,內核會逐個檢查全部活動進程,以判斷它是不是正要終結的進程的子進程,若是是,則將其的父進程id 改成1

​ 注意,孤兒進程並不會變爲殭屍進程,由於它終結的時候init進程一定會調用一個wait函數取得其終止狀態。

殭屍進程

​ 終止進程的父進程調用wait或者waitpid時,能夠獲得一些子進程的信息,包括進程id,終止狀態和CPU時間總量。內核會根據這些信息釋放終止進程的存儲區,關閉其全部打開的文件。

​ 一個已經終止,可是父進程還沒有對其進行善後處理的進程稱爲殭屍進程。

//老子不收屍,兒子變殭屍
//孤兒不用怕,養父是老大
//生個孫子當龍子,哈哈哈
//簡單的例子,不須要等待孫子進程,孫子進程也不會變成殭屍進程
if(fork())
{
 //進程1處理進程2結束
  wait(NULL);
}
else
{
    //進程2fork出進程3再終結
    if(fork())
    {
        exit();
    }
    else
    {
        //成功得到養父進程init
    }
    
}
複製代碼

5.4 進程結束處理

​ 當一個進程正常或異常終止的時候,內核就會向其父進程發送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);	//阻塞方式
複製代碼

5.5 函數exec

​ 當進程調用一種exec的時候,該進程執行的程序徹底替換成爲新程序,而新程序則從其main函數開始執行,由於調用exec並不建立新進程,先後的進程id未改變,exec只是用磁盤上的一個新進程替換了當前進程的正文段,數據段,堆段和棧段。

​ 用exec能夠初始執行新的程序,exit函數和wait函數處理終止和等待終止。

exec
//exec(l,v)(p)
//l表示將參數傳入到函數參數中
//v表示將參數定義爲一個傳入到函數的參數中
//p表明須要自定義環境變量,若沒有p則繼承進程的環境變量
int system(const char *cmdstring)
    //會調用fork,返回該進程的終止狀態
    至關於在命令行中直接輸入cmdstring
複製代碼

5.6 更改用戶id和更改用戶組id

​ 注意須要有超級權限

int setuid(uid_t uid) //設置用戶id int setgid(gid_t gid) //設置用戶組id 複製代碼
相關文章
相關標籤/搜索