進程控制-進程終止(exit、_exit)

知道了進程怎麼創建,接下來就來看看怎麼終止一個進程終止函數exit()和_exit()。

函數 頭文件 聲明
exit stdlib.h void exit(int status)
_exit unistd.h void _exit(int status)

參數作用:可以利用這個參數傳遞進程退出狀態。0表示正常退出,其他情況表示非正常結束。可以用wait接受來自子進程的退出碼。在任意
⼀種情況下,該終⽌狀態的⽗進程都能使⽤wait或waitpid函數取得其終⽌狀態
1. exit()、_exit()
首先,exit()和_exit()都是用來終止進程的。當進程執行exit()或_exit()函數時,系統會立即停止所以操作,清除進程相關的各種數據結構,包括進程PCB,並且終止當前進程的運行。


這裏寫圖片描述

可以看到,exit()函數的作用最爲簡單:直接使進程停止運行,清除其使用的內存空間,並銷燬其在內核中的各種數據結構;exit() 函數則在這些基礎上作了一些包裝,在執行退出之前加了若干道工序,也是因爲這個原因,有些人認爲exit已經不能算是純粹的系統調用。
那麼exit()在結束調用它的進程之前都做了什麼呢?

     exit調用系統調用exit()前先對文件的打開情況進行檢查,關閉所有打開的流,這將導致寫所有被緩衝的輸出,刪除用TMPFILE函數建立的所有臨時文件。這裏的緩衝就是圖中的“I/O緩衝”。系統會爲每個已經打開的文件,在內存定義一片緩衝區,這樣我們每次寫入數據都是先寫入到緩衝區中,然後滿足一定條件後再由緩衝區一次性寫入到文件中。這樣大大加快了文件讀寫的速度。
   
     簡單的說就是,exit函數將終止調用進程。在退出程序之前,所有文件關閉,緩衝輸出內容將刷新定義,並調用所有已刷新的“出口函數”(由atexit定義)。
而_exit同樣終止調用進程,但不關閉文件,不清除輸出緩存,也不調用出口函數。
所以當文件進行讀寫操作後,數據有可能在緩衝區,而這時候調用_exit則會將緩衝區的數據丟失,使得出現錯誤,數據不完整。因此當讀寫文件操作後,儘量使用exit()終止進程,避免意想不到的錯誤。
使用實例:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
 pid_t id = fork();
 if( id < 0 ){
 exit(1);
 }
 else if( id == 0 ){
 printf("clild is run, child pid is : %d\n",getpid());
 sleep(1);
 exit(100);
 }
 else{
 printf("father is run,father pid is : %d\n",getpid());
 }
 int ret;
 if( wait(&ret) < 0 ){
 printf("wait error, error code is : %d\n", errno);
 return 1;
 }
 printf("wait success, status code is : %d\n",ret);
 return 0;
}

結果:
這裏寫圖片描述
2. 進程終止的特殊情況
1)子進程終止時,父進程並不正在執行 wait()調用。
2)當子進程尚未終止時,父進程卻終止了。
在第一種情況中,要終止的進程就處於一種過渡狀態 ,稱爲 僵死狀態(zombie ),處於這種狀態的進程不使用任何內核資源,但是要佔用內核中的進程處理表那的一項。當其父進程執行wait()等待子進程時,它會進入睡眠狀態,然後把這種處於過渡狀態的進程從系統內刪除,父進程仍將能得到該子進程的結束狀態。
第二種情況中,對於⽗進程已經終⽌的所有子進程,他們的⽗進程都改變爲init進程。我們稱這些子進程由init領養。⼀個init的⼦進程(包括領養進程)終⽌時, init會調⽤⼀個wait函數取得其終⽌狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章