1. 程式人生 > >開啟子程序的兩種方式、程序間記憶體空間隔離、程序物件的方法或屬性詳解

開啟子程序的兩種方式、程序間記憶體空間隔離、程序物件的方法或屬性詳解

1、作業系統(推薦檢視書籍:現代作業系統)
    作業系統是位於計算機硬體與軟體之間的控制程式
    作用:
        1、將硬體的複雜操作封裝成簡單的介面,給使用者或者應用程式使用
        2、將多個應用程式對硬體的競爭變的有序
2、程序
    一個正在執行的程式或者說是一個程式的執行過程
3、序列、併發、並行
    序列:一個任務完完整整執行完畢,才執行下一個程式
    併發:多個任務看起來是同時執行的效果,單核就可以實現併發
    並行:多個任務真正意義上同時執行的效果,多核才可以實現並行
4、多道技術
    背景:想要在單核下實現併發(單核同一時刻只能執行一個任務)
    併發實現的本質就是:切換+儲存狀態
    多道技術:
        1、空間上的複用——》多個任務共用一個記憶體條,但佔用記憶體是彼此隔離的,而且是物理層面隔離
        2、時間上的複用——》多個任務共用同一個CPU
           切換:
                1、遇到I/O操作進行切換

                2、一個任務佔用CPU時間過長,或者有另外一個優先順序更高的任務搶走cpu執行許可權

一、程序與程式的區別:

程序是正在進行的一個過程,或者說是一個任務,負責執行任務的是CPU。
程式只是執行 任務背後實施的程式碼

注意:同一個程式執行兩次,是兩個程序,如開啟兩個QQ,一個大號,一個小號,只是程序名一樣罷了。

二、併發與並行:

無論是併發還是並行,在使用者使用時感覺都差不多,不管是程序還是執行緒,都只是任務而已,真正驅使任務幹活的是cpu,而單個cpu只能同時執行一個任務。

1.併發:是偽並行,看起來是同時執行。單個cpu+多道技術就可以實現併發(並行也屬於併發)
2.並行:同時執行,具有多個cpu才能實現並行效果


三、關於建立子程序

    1.相同的是:程序建立後,父程序和子程序都有各自不同的地址空間(多道技術要求物理層面實現程序之間記憶體的隔離),任何一個程序其在地址空間中的修改都不會影響到另外一個程序。
    2.不同的是:在UNIX中,子程序的初始地址空間是父程序的一個副本。提示:子程序和父程序是可以有隻讀的共享記憶體區的。但是對於windows系統來說,從一開始父程序和子程序的地址空間是不同的。

四、程序的終止

1.正常退出(自願,如使用者點選程式上的關閉按鈕或者程式執行完畢呼叫發起系統呼叫正常退出,在linux用exit,在windows中用ExitProcess)
2.出錯退出(自願,python a.py中a.py不存在)
3.嚴重錯誤(非自願,執行非法指令,如引用不存在的記憶體,1/0等,可以捕捉異常,try.....except...)
4.被其他程序殺死(非自願,如kill -9)

五、程序的層次結構

無論UNIX還是windows,只能有一個父程序,不同的區別:
1.UNIX中所有課程都是以init程序為根,組成樹形結構。父、子程序共同組成一個程序組,當有訊號指令傳送過來後會將送達到程序組內所有成員。
2.在windows中,沒有程序層次的概念,所有程序都是地位相同的,唯一類似於程序層次的暗示,是在建立程序時,父程序得到一個特別的令牌(稱為控制代碼),該控制代碼可以用來控制子程序,但是父程序有權把該控制代碼傳給其他子程序,因此沒有層次之說。

六、程序的狀態

兩種情況下會導致一個程序在邏輯上的不能執行:
1、程序掛起是自身原因,遇到I/O阻塞,便要讓出CPU為其它程序執行任務,這樣就保證CPU隨時保持工作狀態
2、與程序無關,與作業系統有關。由於一個程序佔用時間過長,或者優先順序更高任務切換進入等原因,而呼叫其他的程序去使用CPU

程序具有有有三種狀態:

七、程序併發的實現

程序併發的實現在於,硬體中斷一個正在執行的程序,把此時程序執行的所有狀態儲存下來,為此,作業系統維護一張表格,即程序表(process table),每個程序佔用一個程序表項(這些表項稱為程序控制塊)



該表存放了程序狀態的重要資訊:程式計數器、堆疊指標、記憶體分配狀態、所有開啟檔案的狀態、賬號和高度資訊,以及其他程序由執行態轉為就緒態或阻塞態時,必須儲存的資訊,從而保證該程序在兩次啟動時,能夠從上一次斷開處繼續進一步操作,而沒有任何影響。

python併發程式設計之多程序

multiprocessing模組:用來開啟子程序並在子程序中執行我們定製的任務(比如函式),該模組與多執行緒模組threading的程式設計介面類似.
multiprocessing模組的功能眾多:支援子程序、通訊和共享資料、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等元件。
    需要再次強調的一點是:與執行緒不同,程序沒有任何共享狀態,程序修改的資料,改動僅限於該程序內。

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


強調:
1. 需要使用關鍵字的方式來指定引數
2. args指定的為傳給target函式的位置引數,是一個元組形式,必須有逗號
引數介紹:
1 group引數未使用,值始終為None
 
2 target表示呼叫物件,即子程序要執行的任務
 
3 args表示呼叫物件的位置引數元組,args=(1,2,'egon',)
 
4 kwargs表示呼叫物件的字典,kwargs={'name':'egon','age':18}
 
5 name為子程序的名稱
方法介紹:
 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開啟的程序  
屬性介紹:
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字元的字串。這個鍵的用途是為涉及網路
連線的底層程序間通訊提供安全性,這類連線只有在具有相同的身份驗證鍵時才能成功(瞭解即可)
Process類的使用:
注意:在windows中Process()必須放到 # if __name__ == '__main__':下
開啟子程序的兩種方式
方式一:
from multiprocessing import Process
import time
def task(x):
    print('%s is running' %x)
    time.sleep(3)    #為了出現效果而讓子程序睡3秒
    print('%s is done' %x)
if__name__=='__main__':
    #p=Process(target=task,kwargs={'x':'子程序'})    #此例子中不建議使用關鍵字引數
    p=Process(target=task,args=('子程序,'))    #如果args=(),括號內只有一個引數,一定記住加逗號
    p.start()    #只是在作業系統傳送一個開啟子程序的訊號
    print('主')
結果:
主
子程序 is running
'''期間等待3秒多'''
子程序 is done

方式二:
from multiprocessing import Process
import time
class Myprocess(Process):
    def __init__(self,x):
        super().__init__()
        self.name=x
    def run(self):#固定模版,必須使用
        print('%s is running' %self.name)
        time.sleep(3)
        print('%s is done' %self.name)
if __name__=='__main__':
    p=Process('子程序1')
    p.start()    #p.run()
    print('主')
結果:
主
子程序 is running
'''期間等待3秒多'''
子程序 is done

程序之間的記憶體空間是隔離的

from multiprocessing import Process
n=100 #在windows系統中應該把全域性變數定義在if __name__ == '__main__'之上就可以了
def work():
    global n
    n=0
    print('子程序內: ',n)

if __name__ == '__main__':
    p=Process(target=work)
    p.start()
    print('主程序內: ',n)

孤兒程序及殭屍程序概念:
在unix/linux中,正常情況下,子程序是通過父程序建立的,子程序在建立新的程序。子程序的結束和父程序的執行時
一個非同步過程,即父程序永遠無法預測子程序到底什麼結束。當一個程序完成它的工作終止之後,它的父程序需要呼叫
wait()或者waitpid()系統呼叫取得子程序的終止程序。

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

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

舉例:

def task(x,n):
    print('%s is running' % x)
    time.sleep(n)
    print('%s is done' % x)


if __name__ == '__main__':
    # Process(target=task,kwargs={'x':'子程序'})
    p1 = Process(target=task, args=('子程序1',3))  # 如果args=(),括號內只有一個引數,一定記住加逗號
    p2 = Process(target=task, args=('子程序2',5))  # 如果args=(),括號內只有一個引數,一定記住加逗號
    p1.start()  # 只是在作業系統傳送一個開啟子程序的訊號
    p2.start()  # 只是在作業系統傳送一個開啟子程序的訊號

    print('主')
from multiprocessing import Process
import time

x=100
def task():
    global x
    x=0
    print('done')
if __name__ == '__main__':
    p=Process(target=task)
    p.start()
    time.sleep(5) # 讓父程序在原地等待,等了5s後,才執行下一行程式碼
    print(x)

程序物件的方法或屬性詳解


from multiprocessing import Process
import time
import os

def task():
    # 檢視子程序號及父程序號
    print('自己的id:%s 父程序的id:%s ' %(os.getpid(),os.getppid()))
    time.sleep(200)

if __name__ == '__main__':
    p1=Process(target=task)
    p1.start()
    # 檢視父程序號及執行此程式碼的pycharm程序號
    print('主',os.getpid(),os.getppid())
    # 爹=》主--》兒子

瞭解: 

from multiprocessing import Process,current_process
import time

def task():
    # 檢視子程序名
    print('子程序[%s]執行。。。。' %current_process().name) 
    time.sleep(200)

if __name__ == '__main__':
    # 自定義程序名
    p1=Process(target=task,name='子程序1')
    p1.start()
    # 檢視子程序名
    # print(p1.name)
    print('主')
from multiprocessing import Process,current_process
import time

def task():
    print('子程序[%s]執行。。。。' %current_process().name)
    time.sleep(2)

if __name__ == '__main__':
    p1=Process(target=task,name='子程序1')
    p1.start()
    # is_alive:檢視程序是否存活
    # print(p1.is_alive())
    # p1.join()
    # print(p1.is_alive())
    # 終止程序,terminate和start是應用程式發訊號,讓作業系統執行操作
    p1.terminate()
    time.sleep(1)
    print(p1.is_alive())
    print('主')