1. 程式人生 > >day30 程序介紹,程序簡單應用,join,空間隔離

day30 程序介紹,程序簡單應用,join,空間隔離

  一. 相關背景知識:

  作業系統就是一個協調、管理和控制計算機硬體資源和軟體資源的控制程式。

  作業系統的功能:

  1. 隱藏了醜陋的硬體呼叫介面(鍵盤、滑鼠、音箱等等怎麼實現的,就不需要你管了),為應用程式設計師提供呼叫硬體資源的更好,更簡單,更清晰的模型(系統呼叫介面)  2. 將應用程式對硬體資源的競態請求變得有序化

  

  多道技術:

    1. 產生背景:針對單核,實現併發   2. 空間上的複用(將記憶體分為幾部分,每個部分放入一個程式,這樣,同一時間記憶體中就有了多道程式):如記憶體中同時有多道程式  3. 時間上的複用(當一個程式在等待I/O時,另一個程式可以使用cpu,如果記憶體中可以同時存放足夠多的作業,則cpu的利用率可以接近100%):複用一個cpu的時間片

  

  二. 什麼是程序

  程序(Process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。

  在早期面向程序設計的計算機結構中,程序是程式的基本執行實體;在當代面向執行緒設計的計算機結構中,程序是執行緒的容器

  程式是指令、資料及其組織形式的描述,程序是程式的實體。

  狹義定義:程序是正在執行的程式的例項(an instance of a computer program that is being executed)。

  廣義定義:程序是一個具有一定獨立功能的程式關於某個資料集合的一次執行活動。它是作業系統動態執行的基本單元,在傳統的作業系統中,程序既是基本的分配單元,也是基本的執行單元。 
動態性:程序的實質是程式在多道程式系統中的一次執行過程,程序是動態產生,動態消亡的。
併發性:任何程序都可以同其他程序一起併發執行
獨立性:程序是一個能獨立執行的基本單位,同時也是系統分配資源和排程的獨立單位;
非同步性:由於程序間的相互制約,使程序具有執行的間斷性,即程序按各自獨立的、不可預知的速度向前推進
結構特徵:程序由程式、資料和程序控制塊三部分組成。
多個不同的程序可以包含相同的程式:一個程式在不同的資料集裡就構成不同的程序,能得到不同的結果;但是執行過程中,程式不能發生改變。
程序的特徵
程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。
而程序是程式在處理機上的一次執行過程,它是一個動態的概念。
程式可以作為一種軟體資料長期存在,而程序是有一定生命期的。
程式是永久的,程序是暫時的。
舉例:就像qq一樣,qq是我們安裝在自己電腦上的客戶端程式,其實就是一堆的程式碼檔案,我們不執行qq,那麼他就是一堆程式碼程式,當我們執行qq的時候,這些程式碼執行起來,就成為一個程序了。
程式和程序的區別

  併發: 是偽並行, 即看起來同時執行. 單個cpu多道技術可以實現併發

  並行: 同時執行, 多個cpu可以實現並行

  

  

  三. 同步\非同步\阻塞\非阻塞(重點)

   1. 程序的狀態

  (1)就緒(Ready)狀態: 當程序已分配到除CPU以外的所有必要的資源,只要獲得處理機便可立即執行,這時的程序狀態稱為就緒狀態。

  (2)執行/執行(Running)狀態: 當程序已獲得處理機,其程式正在處理機上執行,此時的程序狀態稱為執行狀態。

  (3)阻塞(Blocked)狀態: 正在執行的程序,由於等待某個事件發生而無法執行時,便放棄處理機而處於阻塞狀態。引起程序阻塞的事件可有多種,例如,等待I/O完成、申請緩衝區不能滿足、等待信件(訊號)等。

    事件請求:input、sleep、檔案輸入輸出、recv、accept等

    事件發生:sleep、input等完成了  

    完成上面的操作後, 獲得時間片, 重新今日就緒狀態

    

    2. 同步\非同步

    同步: 一個任務的完成需要等待另一個任務的完成後才能完成, 即序列的形式, 這是一種可靠的任務序列. 

比如我們去樓下的老家肉餅吃飯,飯點好了,取餐的時候發生了一些同步非同步的事情。
同步:我們都站在隊裡等著取餐,前面有個人點了一份肉餅,後廚做了很久,但是由於同步機制,我們還是要站在隊裡等著前面那個人的肉餅做好取走,我們才往前走一步。
非同步:我們點完餐之後,點餐員給了我們一個取餐號碼,跟你說,你不用在這裡排隊等著,去找個地方坐著玩手機去吧,等飯做好了,我叫你。這種機制(等待別人通知)就是非同步等待訊息通知。在非同步訊息處理中,等待訊息通知者(在這個例子中等著取餐的你)往往註冊一個回撥機制,在所等待的事件被觸發時由觸發機制(點餐員)通過某種機制(喊號,‘250號你的包子好了‘)找到等待該事件的人。
舉例

   3. 阻塞\非阻塞

    從程式(執行緒)等待訊息通知時的狀態角度, 阻塞和非阻塞定義了兩種狀態(無所謂同步或者非同步).

舉例
繼續上面的那個例子,不論是排隊還是使用號碼等待通知,如果在這個等待的過程中,等待者除了等待訊息通知之外不能做其它的事情,那麼該機制就是阻塞的,表現在程式中,也就是該程式一直阻塞在該函式呼叫處不能繼續往下執行。
相反,有的人喜歡在等待取餐的時候一邊打遊戲一邊等待,這樣的狀態就是非阻塞的,因為他(等待者)沒有阻塞在這個訊息通知上,而是一邊做自己的事情一邊等待。阻塞的方法:input、time.sleep,socket中的recv、accept等等。

   4. 組合的四種形式

1. 同步阻塞形式
    效率最低。拿上面的例子來說,就是你專心排隊,什麼別的事都不做。

2. 非同步阻塞形式
    如果在排隊取餐的人採用的是非同步的方式去等待訊息被觸發(通知),也就是領了一張小紙條,假如在這段時間裡他不能做其它的事情,就在那坐著等著,不能玩遊戲等,那麼很顯然,這個人被阻塞在了這個等待的操作上面;

    非同步操作是可以被阻塞住的,只不過它不是在處理訊息時阻塞,而是在等待訊息通知時被阻塞。

3. 同步非阻塞形式
    實際上是效率低下的。

    想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有,如果把打電話和觀察排隊的位置看成是程式的兩個操作的話,這個程式需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的。

4. 非同步非阻塞形式
    效率更高,

    因為打電話是你(等待者)的事情,而通知你則是櫃檯(訊息觸發機制)的事情,程式沒有在兩種不同的操作中來回切換。

    比如說,這個人突然發覺自己煙癮犯了,需要出去抽根菸,於是他告訴點餐員說,排到我這個號碼的時候麻煩到外面通知我一下,那麼他就沒有被阻塞在這個等待的操作上面,自然這個就是非同步+非阻塞的方式了。

  很多人會把同步和阻塞混淆,是因為很多時候同步操作會以阻塞的形式表現出來,同樣的,很多人也會把非同步和非阻塞混淆,因為非同步操作一般都不會在真正的IO操作處被阻塞。
View Code

   5. 程序的建立:

  新程序的建立都是由一個已經存在的程序執行了一個用於建立程序的系統呼叫而建立的:

1. 在UNIX中該系統呼叫是:fork,fork會建立一個與父程序一模一樣的副本,二者有相同的儲存映像、同樣的環境字串和同樣的開啟檔案(在shell直譯器程序中,執行一個命令就會建立一個子程序)

2. 在windows中該系統呼叫是:CreateProcess,CreateProcess既處理程序的建立,也負責把正確的程式裝入新程序。
不同系統的程序建立呼叫

  關於建立的子程序,UNIX和windows

1.相同的是:程序建立後,父程序和子程序有各自不同的地址空間(多道技術要求物理層面實現程序之間記憶體的隔離),任何一個程序的在其地址空間中的修改都不會影響到另外一個程序。

2.不同的是:在UNIX中,子程序的初始地址空間是父程序的一個副本,提示:子程序和父程序是可以有隻讀的共享記憶體區的。但是對於windows系統來說,從一開始父程序與子程序的地址空間就是不同的。
子程序

 

  四. multiprocess模組   

   這是Python一個操作, 管理程序的包, 幾乎包含和程序有關的操作, 主要分為四部分: 建立程序, 程序同步, 程序池, 程序間資料共享.

   1. 程序建立模組 process

    Process([group [, target [, name [, args [, kwargs]]]]]),由該類例項化得到的物件,表示一個子程序中的任務(尚未啟動)

    強調:1. 需要使用關鍵字的方式來指定引數    2. args指定的為傳給target函式的位置引數,是一個元組形式,必須有逗號

#當前檔名稱為test.py
# from multiprocessing import Process
#
# def func():
#     print(12345)
#
# if __name__ == '__main__': #windows 下才需要寫這個,這和系統建立程序的機制有關係,不用深究,記著windows下要寫就好啦
#     #首先我運行當前這個test.py檔案,執行這個檔案的程式,那麼就產生了程序,這個程序我們稱為主程序
#
#     p = Process(target=func,) #將函式註冊到一個程序中,p是一個程序物件,此時還沒有啟動程序,只是建立了一個程序物件。並且func是不加括號的,因為加上括號這個函式就直接運行了對吧。
#     p.start() #告訴作業系統,給我開啟一個程序,func這個函式就被我們新開的這個程序執行了,而這個程序是我主程序執行過程中創建出來的,所以稱這個新建立的程序為主程序的子程序,而主程序又可以稱為這個新程序的父程序。
          #而這個子程序中執行的程式,相當於將現在這個test.py檔案中的程式copy到一個你看不到的python檔案中去執行了,就相當於當前這個檔案,被另外一個py檔案import過去並執行了。
          #start並不是直接就去執行了,我們知道程序有三個狀態,程序會進入程序的三個狀態,就緒,(被排程,也就是時間片切換到它的時候)執行,阻塞,並且在這個三個狀態之間不斷的轉換,等待cpu執行時間片到了。
#     print('*' * 10) #這是主程序的程式,上面開啟的子程序的程式是和主程序的程式同時執行的,我們稱為非同步
示例

    主程序建立子程序是非同步執行的, 可以通過程序ID驗證

import time
import os

#os.getpid()  獲取自己程序的ID號
#os.getppid() 獲取自己程序的父程序的ID號

from multiprocessing import Process

def func():
    print('aaaa')
    time.sleep(1)
    print('子程序>>',os.getpid())
    print('該子程序的父程序>>',os.getppid())
    print(12345)

if __name__ == '__main__': 
    #首先我運行當前這個檔案,執行的這個檔案的程式,那麼就產生了主程序

    p = Process(target=func,) 
    p.start() 
    print('*' * 10) 
    print('父程序>>',os.getpid())
    print('父程序的父程序>>',os.getppid())

#加上time和程序號給大家看一看結果:
#********** 首先打印出來了主程序的程式,然後列印的是子程序的,也就是子程序是非同步執行的,相當於主程序和子程序同時執行著,如果是同步的話,我們先執行的是func(),然後再列印主程序最後的10個*號。
#父程序>> 3308
#父程序的父程序>> 5916 #我執行的test.py檔案的父程序號,它是pycharm的程序號,工作管理員可以看到

#aaaa
#子程序>> 4536
#該子程序的父程序>> 3308 #是我主程序的ID號,說明主程序為它的父程序

#12345
id驗證非同步

    Process類中引數介紹:

引數介紹:
1 group引數未使用,值始終為None
2 target表示呼叫物件,即子程序要執行的任務
3 args表示呼叫物件的位置引數元組,args=(1,2,'egon',)
4 kwargs表示呼叫物件的字典,kwargs={'name':'egon','age':18}
5 name為子程序的名稱
引數介紹

    Process類中方法介紹:

1 p.start():啟動程序,並呼叫該子程序中的p.run() 
2 p.run():程序啟動時執行的方法,正是它去呼叫target指定的函式,我們自定義類的類中一定要實現該方法  
3 p.terminate():強制終止程序p,不會進行任何清理操作,如果p建立了子程序,該子程序就成了殭屍程序,使用該方法需要特別小心這種情況。如果p還儲存了一個鎖那麼也將不會被釋放,進而導致死鎖
4 p.is_alive():如果p仍然執行,返回True
5 p.join([timeout]):主執行緒等待p終止(強調:是主執行緒處於等的狀態,而p是處於執行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的程序,而不能join住run開啟的程序  
方法介紹

    Process類中自帶的屬性

1 p.daemon:預設值為False,如果設為True,代表p為後臺執行的守護程序,當p的父程序終止時,p也隨之終止,並且設定為True後,p不能建立自己的新程序,必須在p.start()之前設定
2 p.name:程序的名稱
3 p.pid:程序的pid
4 p.exitcode:程序在執行時為None、如果為–N,表示被訊號N結束(瞭解即可)
5 p.authkey:程序的身份驗證鍵,預設是由os.urandom()隨機生成的32字元的字串。這個鍵的用途是為涉及網路連線的底層程序間通訊提供安全性,這類連線只有在具有相同的身份驗證鍵時才能成功(瞭解即可)
屬性介紹

   2. join方法

   join 在主程序上阻塞, 等待子程序執行完畢, 再繼續執行主程序

#下面的註釋按照編號去看,別忘啦!
import time
import os
from multiprocessing import Process

def func(x,y):
    print(x)
    # time.sleep(1) #程序切換:如果沒有這個時間間隔,那麼你會發現func執行結果是列印一個x然後一個y,再列印一個x一個y,不會出現列印多個x然後列印y的情況,因為兩個列印距離太近了而且執行的也非常快,但是如果你這段程式執行慢的話,你就會發現程序之間的切換了。
    print(y)

if __name__ == '__main__':

    p_list= []
    for i in range(10):
        p = Process(target=func,args=('姑娘%s'%i,'來玩啊!'))
        p_list.append(p)
        p.start()

    [ap.join() for ap in p_list] #4、這是解決辦法,前提是我們的子程序全部都已經去執行了,那麼我在一次給所有正在執行的子程序加上join,那麼主程序就需要等著所有子程序執行結束才會繼續執行自己的程式了,並且保障了所有子程序是非同步執行的。

        # p.join() #1、如果加到for迴圈裡面,那麼所有子程序包括父程序就全部變為同步了,因為for迴圈也是主程序的,迴圈第一次的時候,一個程序去執行了,然後這個程序就join住了,那麼for迴圈就不會繼續執行了,等著第一個子程序執行結束才會繼續執行for迴圈去建立第二個子程序。
        #2、如果我不想這樣的,也就是我想所有的子程序是非同步的,然後所有的子程序執行完了再執行主程序
    #p.join() #3、如果這樣寫的話,多次執行之後,你會發現會出現主程序的程式比一些子程序先執行完,因為我們p.join()是對最後一個子程序進行了join,也就是說如果這最後一個子程序先於其他子程序執行完,那麼主程序就會去執行,而此時如果還有一些子程序沒有執行完,而主程序執行
         #完了,那麼就會先列印主程序的內容了,這個cpu排程程序的機制有關係,因為我們的電腦可能只有4個cpu,我的子程序加上住程序有11個,雖然我for迴圈是按順序起程序的,但是作業系統一定會按照順序給你執行你的程序嗎,答案是不會的,作業系統會按照自己的演算法來分配進
              #程給cpu去執行,這裡也解釋了我們打印出來的子程序中的內容也是沒有固定順序的原因,因為列印結果也需要呼叫cpu,可以理解成程序在爭搶cpu,如果同學你想問這是什麼演算法,這就要去研究作業系統啦。那我們的想所有子程序非同步執行,然後再執行主程序的這個需求怎麼解決啊
    print('不要錢~~~~~~~~~~~~~~~~!')

程式碼
join方法

   3. 第二種程序建立方法(繼承)

class MyProcess(Process): #自己寫一個類,繼承Process類
    #我們通過init方法可以傳引數,如果只寫一個run方法,那麼沒法傳引數,因為建立物件的是傳參就是在init方法裡面,面向物件的時候,我們是不是學過
    def __init__(self,person):
        super().__init__()    # 直接呼叫父類的構造方法才能新增新的屬性
        self.person=person
    def run(self):
        print(os.getpid())
        print(self.pid)
        print(self.pid)
        print('%s 正在和女主播聊天' %self.person)
    # def start(self):
    #     #如果你非要寫一個start方法,可以這樣寫,並且在run方法前後,可以寫一些其他的邏輯
    #     self.run()
if __name__ == '__main__':
    p1=MyProcess('Jedan')
    p2=MyProcess('太白')
    p3=MyProcess('alexDSB')

    p1.start() #start內部會自動呼叫run方法
    p2.start()
    # p2.run()
    p3.start()


    p1.join()
    p2.join()
    p3.join()
程序建立方法2

   4. Process物件的其他方法和屬性(瞭解)

#程序物件的其他方法一:terminate,is_alive
from multiprocessing import Process
import time
import random

class Piao(Process):
    def __init__(self,name):
        self.name=name
        super().__init__()

    def run(self):
        print('%s is 打飛機' %self.name)
        # s = input('???') #別忘了再pycharm下子程序中不能input輸入,會報錯EOFError: EOF when reading a line,因為子程序中沒有像我們主程序這樣的在pycharm下的控制檯可以輸入東西的地方
        time.sleep(2)
        print('%s is 打飛機結束' %self.name)

if __name__ == '__main__':
    p1=Piao('太白')
    p1.start()
    time.sleep(5)
    p1.terminate()#關閉程序,不會立即關閉,有個等著作業系統去關閉這個程序的時間,所以is_alive立刻檢視的結果可能還是存活,但是稍微等一會,就被關掉了
    print(p1.is_alive()) #結果為True
    print('等會。。。。')
    time.sleep(1)
    print(p1.is_alive()) #結果為False
terminate, is_alieve
from multiprocessing import Process
import time
import random
class Piao(Process):
    def __init__(self,name):
        # self.name=name
        # super().__init__() #Process的__init__方法會執行self.name=Piao-1,
        #                    #所以加到這裡,會覆蓋我們的self.name=name

        #為我們開啟的程序設定名字的做法
        super().__init__()
        self.name=name

    def run(self):
        print('%s is piaoing' %self.name)
        time.sleep(random.randrange(1,3))
        print('%s is piao end' %self.name)

p=Piao('egon')
p.start()
print('開始')
print(p.pid) #檢視pid
name, pid

   5. 驗證程序之間的空間隔離

import time
from multiprocessing import Process

num = 100
def func1():
    global num
    time.sleep(1)
    num = num - 1
    print('>>>>>>>>',num)

if __name__ == '__main__':

    p_list = []
    for i in range(10):    # 迴圈建立多個子程序
        p = Process(target=func1,)
        p.start()
        p_list.append(p)    # 把子程序新增進列表

    for pp in p_list:    # 迴圈子程序
        pp.join()    # 每個子程序都阻塞

    print('主程序的num>>>>>',num)    # 跳出迴圈, 即每個子程序都執行完, 後列印主程序的num, 結果還是100, 並未被子程序改變, 證明程序的空間隔離了
    print('主程序結束')    
空間隔離

   6. 殭屍程序和孤兒程序

參考部落格:http://www.cnblogs.com/Anker/p/3271773.html

一:殭屍程序(有害)
  殭屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序並沒有呼叫wait或waitpid獲取子程序的狀態資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。詳解如下

我們知道在unix/linux中,正常情況下子程序是通過父程序建立的,子程序在建立新的程序。子程序的結束和父程序的執行是一個非同步過程,即父程序永遠無法預測子程序到底什麼時候結束,如果子程序一結束就立刻回收其全部資源,那麼在父程序內將無法獲取子程序的狀態資訊。

因此,UNⅨ提供了一種機制可以保證父程序可以在任意時刻獲取子程序結束時的狀態資訊:
1、在每個程序退出的時候,核心釋放該程序所有的資源,包括開啟的檔案,佔用的記憶體等。但是仍然為其保留一定的資訊(包括程序號the process ID,退出狀態the termination status of the process,執行時間the amount of CPU time taken by the process等)
2、直到父程序通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果程序不呼叫wait / waitpid的話,那麼保留的那段資訊就不會釋放,其程序號就會一直被佔用,但是系統所能使用的程序號是有限的,如果大量的產生僵死程序,將因為沒有可用的程序號而導致系統不能產生新的程序. 此即為殭屍程序的危害,應當避免。

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

二:孤兒程序(無害)

  孤兒程序:一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。

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

我們來測試一下(建立完子程序後,主程序所在的這個指令碼就退出了,當父程序先於子程序結束時,子程序會被init收養,成為孤兒程序,而非殭屍程序),檔案內容

import os
import sys
import time

pid = os.getpid()
ppid = os.getppid()
print 'im father', 'pid', pid, 'ppid', ppid
pid = os.fork()
#執行pid=os.fork()則會生成一個子程序
#返回值pid有兩種值:
#    如果返回的pid值為0,表示在子程序當中
#    如果返回的pid值>0,表示在父程序當中
if pid > 0:
    print 'father died..'
    sys.exit(0)

# 保證主執行緒退出完畢
time.sleep(1)
print 'im child', os.getpid(), os.getppid()

執行檔案,輸出結果:
im father pid 32515 ppid 32015
father died..
im child 32516 1

看,子程序已經被pid為1的init程序接收了,所以殭屍程序在這種情況下是不存在的,存在只有孤兒程序而已,孤兒程序宣告週期結束自然會被init來銷燬。


三:殭屍程序危害場景:

  例如有個程序,它定期的產 生一個子程序,這個子程序需要做的事情很少,做完它該做的事情之後就退出了,因此這個子程序的生命週期很短,但是,父程序只管生成新的子程序,至於子程序 退出之後的事情,則一概不聞不問,這樣,系統執行上一段時間之後,系統中就會存在很多的僵死程序,倘若用ps命令檢視的話,就會看到很多狀態為Z的程序。 嚴格地來說,僵死程序並不是問題的根源,罪魁禍首是產生出大量僵死程序的那個父程序。因此,當我們尋求如何消滅系統中大量的僵死程序時,答案就是把產生大 量僵死程序的那個元凶槍斃掉(也就是通過kill傳送SIGTERM或者SIGKILL訊號啦)。槍斃了元凶程序之後,它產生的僵死程序就變成了孤兒進 程,這些孤兒程序會被init程序接管,init程序會wait()這些孤兒程序,釋放它們佔用的系統程序表中的資源,這樣,這些已經僵死的孤兒程序 就能瞑目而去了。

四:測試
#1、產生殭屍程序的程式test.py內容如下

#coding:utf-8
from multiprocessing import Process
import time,os

def run():
    print('',os.getpid())

if __name__ == '__main__':
    p=Process(target=run)
    p.start()
    
    print('',os.getpid())
    time.sleep(1000)


#2、在unix或linux系統上執行
[[email protected] ~]# python3  test.py &
[1] 18652
[[email protected]-31-0-19 ~]# 主 18652
子 18653

[[email protected]-31-0-19 ~]# ps aux |grep Z
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     18653  0.0  0.0      0     0 pts/0    Z    20:02   0:00 [python3] <defunct> #出現殭屍程序
root     18656  0.0  0.0 112648   952 pts/0    S+   20:02   0:00 grep --color=auto Z

[[email protected]-31-0-19 ~]# top #執行top命令發現1zombie
top - 20:03:42 up 31 min,  3 users,  load average: 0.01, 0.06, 0.12
Tasks:  93 total,   2 running,  90 sleeping,   0 stopped,   1 zombie
%Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1016884 total,    97184 free,    70848 used,   848852 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   782540 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                        
root      20   0   29788   1256    988 S  0.3  0.1   0:01.50 elfin                                                                                                                      


#3、
等待父程序正常結束後會呼叫wait/waitpid去回收殭屍程序
但如果父程序是一個死迴圈,永遠不會結束,那麼該殭屍程序就會一直存在,殭屍程序過多,就是有害的
解決方法一:殺死父程序
解決方法二:對開啟的子程序應該記得使用join,join會回收殭屍程序
參考python2原始碼註釋
class Process(object):
    def join(self, timeout=None):
        '''
        Wait until child process terminates
        '''
        assert self._parent_pid == os.getpid(), 'can only join a child process'
        assert self._popen is not None, 'can only join a started process'
        res = self._popen.wait(timeout)
        if res is not None:
            _current_process._children.discard(self)

join方法中呼叫了wait,告訴系統釋放殭屍程序。discard為從自己的children中剔除

解決方法三:http://blog.csdn.net/u010571844/article/details/50419798

殭屍程序與孤兒程序
瞭解