天天看點

僵屍程序zombie與孤兒程序orphan 問題提出 示例程式

  以前在學習《unix環境進階程式設計》程序時候,提到孤兒程序和僵屍程序,但是一直對這兩個概念比較模糊。于是今天做了一些測試程式,并把這些記錄下來.

  僵屍程序/僵死程序 in unix system terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie. 在unix 系統中,一個程序結束了,但是他的父程序沒有等待(調用wait / waitpid)他, 那麼他将變成一個僵屍程序。 但是如果該程序的父程序已經先結束了,那麼該程序就不會變成僵屍程序, 因為每個程序結束的時候,系統都會掃描目前系統中所運作的所有程序, 看有沒有哪個程序是剛剛結束的這個程序的子程序,如果是的話,就由init 來接管他,成為他的父程序, 這樣的程序就是下面的孤兒程序.   孤兒程序 相反,如果一個父程序退出,而它的一個或多個子程序還在運作,那麼那些子程序将成為孤兒程序。孤兒程序将被init程序(程序号為1或者0)所收養,并由init程序對它們完成狀态收集工作。

  我們知道在unix/linux中,正常情況下,子程序是通過父程序建立的,子程序在建立新的程序。子程序的結束和父程序的運作是一個異步過程,即父程序永遠無法預測子程序 到底什麼時候結束。 當一個 程序完成它的工作終止之後,它的父程序需要調用wait()或者waitpid()系統調用取得子程序的終止狀态。

  孤兒程序:一個父程序退出,而它的一個或多個子程序還在運作,那麼那些子程序将成為孤兒程序。孤兒程序将被init程序(程序号為1)所收養,并由init程序對它們完成狀态收集工作。

  僵屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序并沒有調用wait或waitpid擷取子程序的狀态資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。

  由于子程序的結束和父程序的運作是一個異步過程,即父程序永遠無法預測子程序到底什麼時候結束.

  那麼會不會因為父程序太忙來不及wait子程序,或者說不知道子程序什麼時候結束,而丢失子程序結束時的狀态資訊呢?

  這種機制就是: 在每個程序退出的時候,核心釋放該程序所有的資源,包括打開的檔案,占用的記憶體等。但是仍然為其保留一定的資訊(包括程序号the process id,退出狀态the termination status of the process,運作時間the amount of cpu time taken by the process等)。直到父程序通過wait / waitpid時才釋放.

但這樣就導緻了問題,我們一般unix系統中,多程序使用的分叉fork并不保證父子程序的執行順序(vfork的父程序與子程序是共用位址空間的,子程序會先運作,但是vfork是被廢棄掉的調用,bsd裡引入它是想避免fork時拷貝父程序的位址空間。是以使用vfork的正常行為是讓子程序馬上調用exec系列函數。但現在使用寫時拷貝,一般不會用vfork了).

由于父子程序運作的順序是不定的,那麼結束的時候也不能确定.是以會出現如下兩種情況 ①父程序先結束,那麼子程序由init程序接管,負責子程序的善後工作,子程序成功孤兒程序. ②子程序先結束,而父程序并沒有調用wait或waitpid擷取子程序的狀态資訊,那麼子程序的程序描述符仍然儲存在系統中,但是成為”黑戶”,并不被init所知,更不能被接管,成為僵死程序。 可見如果父程序程序不調用wait / waitpid的話,那麼保留的子程序那段資訊就不會釋放,其程序号就會一直被占用,但是系統所能使用的程序号是有限的,如果大量的産生僵死程序,将因為沒有可用的程序号而導緻系統不能産生新的程序. 此即為僵屍程序的危害,應當避免。

  孤兒程序是沒有父程序的程序,孤兒程序這個重任就落到了init程序身上,init程序就好像是一個民政局,專門負責處理孤兒程序的善後工作。每當出現一個孤兒程序的時候,核心就把孤兒程序的父程序設定為init,而init程序會循環地wait()它的已經退出的子程序。這樣,當一個孤兒程序凄涼地結束了其生命周期的時候,init程序就會代表黨和政府出面處理它的一切善後工作。是以孤兒程序并不會有什麼危害。

  任何一個子程序(init除外)在exit()之後,并非馬上就消失掉,而是留下一個稱為僵屍程序(zombie)的資料結構,等待父程序處理。這是每個子程序在結束時都要經過的階段。如果子程序在exit()之後,父程序沒有來得及處理,這時用ps指令就能看到子程序的狀态是“z”。如果父程序能及時處理,可能用ps指令就來不及看到子程序的僵屍狀态,但這并不等于子程序不經過僵屍狀态。 如果父程序在子程序結束之前退出,則子程序将由init接管。init将會以父程序的身份對僵屍狀态的子程序進行處理。

 

例如有個程序,它定期的産 生一個子程序,這個子程序需要做的事情很少,做完它該做的事情之後就退出了,是以這個子程序的生命周期很短,但是,父程序隻管生成新的子程序,至于子程序 退出之後的事情,則一概不聞不問,這樣,系統運作上一段時間之後,系統中就會存在很多的僵死程序,倘若用ps指令檢視的話,就會看到很多狀态為z的程序。 嚴格地來說,僵死程序并不是問題的根源,罪魁禍首是産生出大量僵死程序的那個父程序。是以,當我們尋求如何消滅系統中大量的僵死程序時,答案就是把産生大量僵死程序的那個元兇槍斃掉(也就是通過kill發送sigterm或者sigkill信号啦)。槍斃了元兇程序之後,它産生的僵死程序就變成了孤兒程序,這些孤兒程序會被init程序接管,init程序會wait()這些孤兒程序,釋放它們占用的系統程序表中的資源,這樣,這些已經僵死的孤兒程序 就能瞑目而去了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

僵屍程式zombie與孤兒程式orphan 問題提出 示例程式
在這個程式中 父程序退出前,父程序pid=10452,子程序pid=10453 父程序退出後,父程序pid=1792,子程序pid=10453 可以由于是在終端中運作,是以我們的init程序并不是主的init程序,而是user的init程序. 這個很容易了解,我們的程式并不是運作在系統的shell中,而是運作在bash中. 如果進入ctrl+alt+f1,進入指令行中,再次運作程式,就可以看到子程序成為孤兒程序後,父程序成為init[pid = 1]

下面我們修改代碼,讓子程序直接退出.

僵屍程式zombie與孤兒程式orphan 問題提出 示例程式

下面這個程式中,我們将在父程序中循環産生僵屍程序.

僵屍程式zombie與孤兒程式orphan 問題提出 示例程式

76

77

78

79

80

81

82

83

84

85

僵屍程式zombie與孤兒程式orphan 問題提出 示例程式

  《unix 環境進階程式設計》中非常詳細。原理是将子程序成為孤兒程序,進而其的父程序變為init程序,通過init程序可以處理僵屍程序

在這個方法中,我們第一次建立的程序隻是一個過渡程序,它在建立了第二個程序後就立即消亡,進而使第二個程序成為一個孤兒程序,由init接管. 而我們通過讓原程序wait第一個程序來保證第一個程序不會成為僵死程序 由于第一個程序沒做任何操作,便立即退出,是以源程序wait第一個程序的花銷幾乎可以不計. 是以如果一個程序要fork一個子程序,但不要求它等待子程序終止,也不希望子程序處于僵死狀态直到父程序終止,可以使用這種方法. 在這種方法中,父程序和第二個子程序才是真正工作的程序,而第一個子程序隻是用于fork第二個子程序的一個過渡程序,通過它使父程序和第二個子程序的不存在直接的父子關系,進而避免了僵死程序的産生.
僵屍程式zombie與孤兒程式orphan 問題提出 示例程式

轉載:http://blog.csdn.net/gatieme/article/details/50253481