fork復制進程之后,會產生一個進程叫做子進程,被復制的進程就是父進程。不管父進程先結束,還是子進程先結束,對另外一個進程完全沒有影響,父進程和子進程是兩個不同的進程。
一、孤兒進程
現在有以下代碼:
【注意】
上述代碼中,調用getpid()
輸出當前進程的pid,調用getppid()
輸出當前進程的父進程的pid。
讓子進程執行7次,父進程執行3次,編譯并運行,結果如下:
分析:當父進程執行完3次之后,子進程的父進程結束,子進程就變為了孤兒進程,孤兒進程會被系統中的inti(pid=1)收養,但是在目前有些系統中,也會被隨機的其他進程收養。例如,上圖中,父進程執行完3次之后,父進程結束,子進程就變為了孤兒進程,該孤兒進程的父進程的id就變成了1145,說明該孤兒進程的父進程變為了pid為1145的一個進程。
二、僵死進程
僵死進程:子進程先與父進程結束,父進程沒有獲取子進程的退出碼,子進程就變成了僵死進程。
有以下代碼:
編譯以上代碼,并在后臺運行,運行的時候通過ps查看進程信息,結果如下:
分析:
由結果可以看出當子進程結束之后,通過ps查看進程信息的時候還是可以看到子進程的信息,只不過子進程后邊加了一個注釋<defunct>
,表示該子進程變成了僵死進程,僵死進程就是表示代碼已經結束了,是已經結束的進程,這個進程在以后再也不會執行了。按道理子進程在結束了之后不應該還能看到這個進程,也就是說這個子進程本來應該已經死掉了,但是我們還能看到它,這是不合理的。
我們之所以還能看到這個子進程(僵死進程),是因為子進程比父進程先結束,父進程沒有獲取子進程的退出碼,該子進程就變成了僵死進程。
所謂退出碼是存放到PCB中的,當子進程結束之后如果父進程沒有獲取子進程的退出碼,那么子進程的PCB就不會消失,會一直存在,所以在子進程結束之后再查看進程信息的時候就還能看到子進程的信息,但是這個子進程已經是僵死進程了。如果一個進程不斷產生子進程,子進程結束后也沒有獲取子進程的退出碼,那么這些子進程就會變成僵死進程僵死進程就會越來越多,那么內核空間的內存就會被逐漸耗光,而且僵死進程會占用著PID不釋放,那么這個PID就無法被復用,軟件層面的資源也被占著。
三、處理僵死進程
父進程獲取子進程的退出碼,僵死進程就會消失。這一步要通過父進程調用wait()來完成。wait()可以獲取子進程的退出碼,處理僵死進程。
代碼如下:
運行結果如下:
①在前臺運行:
根據運行結果可以看出,一開始只有子進程在執行,因為父進程中執行了wait,子進程執行的時候由于wait阻塞住了父進程,子進程執行結束之后,父進程才開始執行,也就是說父進程在等待子進程結束。
代碼中可以看到無論父進程還是子進程退出碼的值都為3,但是運行結果中val的值并不是3,而是768,這是因為int型變量占4個字節,將3換算為二進制數之后為11,這個11并不會存在第一個字節的起始位置,而是會在4個字節中的任意位置。如下圖所示,把768轉換為二進制數為0011 0000 0000:
將val的值向右移動8個位,將代碼中的printf("val=%d\n",val);
改為printf("val=%d\n",val>>8);
之后,再編譯運行,val的值的結果就是3了,如下圖所示:
②在后臺運行
運行結果:
作為父進程要關注有自己產生的子進程,不能讓它們變為僵死進程。
如果父進程比子進程先結束,父進程沒有通過wait處理結束的子進程,子進程變為了僵死進程,等到父進程結束以后,此時不管子進程活著還是死掉了,就會給子進程重新找一個父進程,讓這個另外的進程來收養子進程,另外的進程來收養子進程的意義就在于這個收養子進程的父進程就會接管這個子進程,然后執行wait獲取該子進程的退出碼,那么即便收養的子進程已經結束變成了僵死進程,通過收養它的父進程來執行wait,這個僵死進程就消失了。