進程關系,父子是否生死相依

眾所周知,進程通常不是憑空獨立的出現的,在類Unix系統中,所有的其他進程都是從 進程0 fork 出來的,每個進程都會擁有多個子進程。那么,想要弄清楚父進程和子進程的關系,我們首先要了解 fork 究竟經歷了什么過程。

fork

我們可以看一看fork的官方文檔。

$man fork

Linux下將會看到:

FORK(2)                       Linux Programmer's Manual                       FORK(2)

NAME
       fork - create a child process

SYNOPSIS
       #include <unistd.h>

       pid_t fork(void);

DESCRIPTION
       fork()  creates  a  new  process  by duplicating the calling process.  The new
       process is referred to as the child process.  The calling process is  referred
       to as the parent process.

       The  child  process  and the parent process run in separate memory spaces.  At
       the time of fork() both memory spaces have the same content.   Memory  writes,
       file  mappings  (mmap(2)),  and unmappings (munmap(2)) performed by one of the
       processes do not affect the other.

The child process is an exact duplicate of the parent process except  for  the
       following points:

       *  The child has its own unique process ID, and this PID does not match the ID
          of any existing process group (setpgid(2)).

       *  The child's parent process ID is the same as the parent's process ID.

       *  The child does not inherit its  parent's  memory  locks  (mlock(2),  mlock‐
          all(2)).

       *  Process   resource   utilizations  (getrusage(2))  and  CPU  time  counters
          (times(2)) are reset to zero in the child.

       *  The child's set of pending signals is initially empty (sigpending(2)).

       *  The  child  does  not  inherit  semaphore  adjustments  from   its   parent
          (semop(2)).

       *  The  child does not inherit process-associated record locks from its parent
          (fcntl(2)).  (On the  other  hand,  it  does  inherit  fcntl(2)  open  file
          description locks and flock(2) locks from its parent.)

       *  The  child does not inherit timers from its parent (setitimer(2), alarm(2),
          timer_create(2)).

       *  The child does not inherit outstanding asynchronous I/O operations from its
          parent  (aio_read(3),  aio_write(3)),  nor does it inherit any asynchronous
          I/O contexts from its parent (see io_setup(2)).


而在Unix環境下則會看到:

FORK(2)                     BSD System Calls Manual                    FORK(2)

NAME
     fork -- create a new process

SYNOPSIS
     #include <unistd.h>

     pid_t
     fork(void);

DESCRIPTION
     fork() causes creation of a new process.  The new process (child process)
     is an exact copy of the calling process (parent process) except for the
     following:

           o   The child process has a unique process ID.

           o   The child process has a different parent process ID (i.e., the
               process ID of the parent process).

           o   The child process has its own copy of the parent's descriptors.
               These descriptors reference the same underlying objects, so
               that, for instance, file pointers in file objects are shared
               between the child and the parent, so that an lseek(2) on a
               descriptor in the child process can affect a subsequent read or
               write by the parent.  This descriptor copying is also used by
               the shell to establish standard input and output for newly cre-
               ated processes as well as to set up pipes.

           o   The child processes resource utilizations are set to 0; see
               setrlimit(2).

可以看到基本內容大同小異,簡單進行翻譯一下:調用fork會創建一個當前進程的精確副本進程,這個被創建出的副本進程被稱作子進程而調用fork的進程則稱為父進程。既然被稱作精確副本,看來子進程和父進程是相同的,其實也不盡然。

比如,子進程將會擁有一個自己的進程標識符也就是所謂的pid。同時,根據父進程的不同,子進程的父進程id也不一樣。

由于現代操作系統的寫時復制機制,即使我們知道每個進程都擁有自己獨立的地址空間,其實指向的物理內存是和父進程相同的(代碼段,數據段,堆棧都指向父親的物理空間),只有子進程修改了其中的某個值時(通常會先調度運行子進程),才會給子進程分配新的物理內存,并把根據情況把新的值或原來的值復制給子進程的內存。

由此可見,父子進程其實有相當的獨立性,并不會相互影響。

那么既然沒有相互影響,那么父子進程會不會有依賴關系了呢?比如,關閉了父進程,子進程還會存在嗎?

既然提出了這個問題,正如手術刀既能治病救人也能殺人滅口,想要了解殺死進程之后發生什么,首先要了解的是我們殺死進程的刀——kill

kill

首先自然是先查看kill的手冊:

$man kill

只摘取linux環境下的內容:

KILL(1)                             User Commands                             KILL(1)

NAME
       kill - send a signal to a process

SYNOPSIS
       kill [options] <pid> [...]

kill聽起來是殺死什么東西的意思,但手冊上卻寫著它只是發送一個信號給進程。其實這個信號指的是Linux標準信號。

Linux標準信號

信號是進程間通信的形式,Linux支持64種標準信號。而其中32種是傳統Unix的信號。可以通過

$kill -l

來查看。

HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS

其中可能與關閉進程有關的信號是INTQUITTERMKILL。讓我們來一一分析一下。

  • SIGINT:其實我們平時在使用終端運行某個軟件的時候,如果這個軟件會持續運行而不再顯示shell提示符,那么我們通常關閉這個程序是使用^C(就是Ctrl+C),其實就是向當前運行的進程發送了一個SIGINT。通知前臺進程組停止進程。也就是會中斷前臺運行的所有進程。
  • SIGQUIT:與SIGINT其實相似,通過終端鍵入^\來發送,相當于錯誤信號,會讓進程產生core文件。
  • SIGTERM:當我們不加任何參數直接調用kill時,則會發送這個信號給進程,這個關閉請求并非強制,它會被阻塞,通常會等待一個程序正常退出。
  • SIGKILL:這個就厲害了,這也是為什么關不掉一個程序,網上通常會教你kill -9,它發送一個強制的關閉信號,不可被忽略。但正是因為這樣,程序難以進行自我清理,而且會產生僵尸進程。

很顯然,由于前三者關閉進程的“人性化”導致出問題的情況及其有限,接下來我們將要對這個兇殘的kill -9做做文章。

在繼續討論之前,我們先來補充一點知識:

僵尸進程和孤兒進程

進程和現實與眾不同的是,進程的世界通常是“白發人送黑發人“,父進程在調用子進程之后,通常會在子進程結束之后進行一些后續處理。

但我們知道,父進程的運行和子進程的結束通常是不可能同時進行的,那父進程也不可能知道子進程是如何結束的,那么,父進程如何為子進程”收尸“呢。

原來每個進程在結束自己之前通常會調用exit()命令,資源即使早就全部釋放了,但進程號,運行時間,退出狀態卻會因此命令而保留,等到父進程調用了waitpid()時,才會釋放這些內容。如果父進程不調用waitpid(),則子進程的信息永遠不會釋放,這就是所謂的僵尸進程

除非,父進程在子進程exit之前就已經關閉,子進程便不會變為僵尸進程,這是因為,每次一個進程結束時,系統都會自動掃描一下這個進程的子進程,如果這個進程有子進程,此時這些子進程被稱作孤兒進程,便會把這些進程轉交給init接管。這些子進程結束后,自然init作為”繼父“進程會以某種機制waitpid()(收尸)的。

讓我們先來構造一個父子關系程序。

#include <stdio.h>
#include <unistd.h>
int main (void) {
    pid_t pid;
    int count = 0;
    pid = fork ();
    if (pid < 0) printf("Error!\n");
    else if (pid == 0) {
        printf("I'm a child\n");
        while(1);
        exit (0);
    }
    else {
        printf("I'm the father, and my son's pid is %d \n",pid);
        while(1);
    }
    exit(0);
}

可見,這個C程序調用了fork()來創建了一個子程序。父子程序都用一個死循環來卡住.

運行效果如下:

I'm the father, and my son's pid is 40211 
I'm a child

使用^C可以看到,發送了SIGINT信號,關閉了父進程和子進程。這是由于SIGINT將會發送給所有依賴于當前終端的進程,自然前臺所有的進程都關閉了。

重新運行,并使用$ps -ef觀看進程列表。

此時使用$pstree -p [父進程的pid]則會看到父子進程的關系。此時無論向父進程發送任何退出信號,都會讓子進程變為孤兒進程。

那么,怎樣才能讓我的子進程隨著父進程愉快地結束呢?

讓我們改寫一下代碼:

#include <signal.h>
#include <sys/prctl.h>
#include <stdio.h>
#include <unistd.h>
int main (void) {
    pid_t pid;
    int count = 0;
    pid = fork ();
    if (pid < 0) printf("Error!\n");
    else if (pid == 0) {
        printf("I'm a child\n");
        while(1) {
            prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
        }

        exit (0);
    }
    else {
        printf("I'm the father, and my son's pid is %d \n",pid);
        while(1) {
        }
    }
    exit(0);
}

這時,殺死父進程,子進程會向自己發送一個SIGKILL信號,從而父子進程都被關閉了。可見父子進程之間的生命相依聯系,就是通過prctl來維系的。這也是程序員可以控制的關系。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容

  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,143評論 0 23
  • 本篇轉自:http://hongjiang.info/why-kill-2-cannot-stop-tomcat/...
    在路上的碼農一枚閱讀 837評論 0 1
  • 1 進程介紹 1.1 進程和程序 所謂進程是由正文段(text)、用戶數據段(user segment)以及系統數...
    瘋狂小王子閱讀 1,252評論 0 7
  • 1.韓麗楓。 她是高中時期班里男生最喜歡的女生——長相甜美,唱歌好聽,愛畫畫,會彈吉他吹口琴拉手風琴。。。 也是班...
    高小花0218閱讀 531評論 0 0
  • 時光會帶走一切,一切的一切。 所以,一個人該留下點什么呢? 還是不留什么,只為了現世快樂一些? 沒有答案!只是可以...
    夜語山林閱讀 283評論 1 1