什麼是進程?linux
進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。算法
第一,進程是一個實體。每個進程都有它本身的地址空間,通常狀況下,包括文本區域(text region)、數據區域(data region)
和堆棧(stack region)。文本區域存儲處理器執行的代碼;數據區域存儲變量和進程執行期間使用的動態分配的內存;堆棧區域存儲着活動過程調用的指令和本地變量。 第二,進程是一個「執行中的程序」。程序是一個沒有生命的實體,只有處理器賦予程序生命時(操做系統執行之),它才能成爲一個活動的實體,咱們稱其爲進程。[3] 進程是操做系統中最基本、重要的概念。是多道程序系統出現後,爲了刻畫系統內部出現的動態狀況,描述系統內部各道程序的活動規律引進的一個概念,
全部多道程序設計操做系統都創建在進程的基礎上。
從理論角度看,是對正在運行的程序過程的抽象; 從實現角度看,是一種數據結構,目的在於清晰地刻畫動態系統的內在規律,有效管理和調度進入計算機系統主存儲器運行的程序。
動態性:進程的實質是程序在多道程序系統中的一次執行過程,進程是動態產生,動態消亡的。 併發性:任何進程均可以同其餘進程一塊兒併發執行 獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位; 異步性:因爲進程間的相互制約,使進程具備執行的間斷性,即進程按各自獨立的、不可預知的速度向前推動 結構特徵:進程由程序、數據和進程控制塊三部分組成。 多個不一樣的進程能夠包含相同的程序:一個程序在不一樣的數據集裏就構成不一樣的進程,能獲得不一樣的結果;可是執行過程當中,程序不能發生改變。
程序是指令和數據的有序集合,其自己沒有任何運行的含義,是一個靜態的概念。 而進程是程序在處理機上的一次執行過程,它是一個動態的概念。 程序能夠做爲一種軟件資料長期存在,而進程是有必定生命期的。 程序是永久的,進程是暫時的。
注意:同一個程序執行兩次,就會在操做系統中出現兩個進程,因此咱們能夠同時運行一個軟件,分別作不一樣的事情也不會混亂。json
在採用多級反饋隊列調度算法的系統中,調度算法的實施過程以下所述。
(1) 應設置多個就緒隊列,併爲各個隊列賦予不一樣的優先級。第一個隊列的優先級最高,第二個隊列次之,
其他各隊列的優先權逐個下降。該算法賦予各個隊列中進程執行時間片的大小也各不相同,在優先權愈高的隊列中,
爲每一個進程所規定的執行時間片就愈小。例如,第二個隊列的時間片要比第一個隊列的時間片長一倍,……,第i+1個
隊列的時間片要比第i個隊列的時間片長一倍。 (2) 當一個新進程進入內存後,首先將它放入第一隊列的末尾,按FCFS原則排隊等待調度。當輪到該進程執行時,
如它能在該時間片內完成,即可準備撤離系統;若是它在一個時間片結束時還沒有完成,調度程序便將該進程轉入第二隊
列的末尾,再一樣地按FCFS原則等待調度執行;若是它在第二隊列中運行一個時間片後仍未完成,再依次將它放入第三
隊列,……,如此下去,當一個長做業(進程)從第一隊列依次降到第n隊列後,在第n 隊列便採起按時間片輪轉的方式運
行。 (3) 僅當第一隊列空閒時,調度程序才調度第二隊列中的進程運行;僅當第1~(i-1)隊列均空時,纔會調度第i隊列中
的進程運行。若是處理機正在第i隊列中爲某進程服務時,又有新進程進入優先權較高的隊列(第1~(i-1)中的任何一個
隊列),則此時新進程將搶佔正在運行進程的處理機,即由調度程序把正在運行的進程放回到第i隊列的末尾,把處理機分
配給新到的高優先權進程。
進程的並行與併發
並行 :是指一個時間段內,有幾個程序都在幾個CPU上運行,任意一個時刻點上,有多個程序在同時運行,而且多道程序之間互不干擾。(資源夠用,好比三個線程,四核的CPU )安全
併發 : 是指一個時間段內,有幾個程序都在同一個CPU上運行,但任意一個時刻點上只有一個程序在處理機上運行,併發是指資源有限的狀況下,二者交替輪流使用資源。服務器
區別:網絡
並行是從微觀上,也就是在一個精確的時間片刻,有不一樣的程序在執行,這就要求必須有多個處理器。
併發是從宏觀上,在一個時間段上能夠看出是同時執行的,好比一個服務器同時處理多個session。session
同步阻塞與非阻塞數據結構
在瞭解其餘概念以前,咱們首先要了解進程的幾個狀態。在程序運行的過程當中,因爲被操做系統的調度算法控制,程序會進入幾個狀態:就緒,運行和阻塞。併發
(1)就緒(Ready)狀態app
當進程已分配到除CPU之外的全部必要的資源,只要得到處理機即可當即執行,這時的進程狀態稱爲就緒狀態。
(2)執行/運行(Running)狀態當進程已得到處理機,其程序正在處理機上執行,此時的進程狀態稱爲執行狀態。
(3)阻塞(Blocked)狀態正在執行的進程,因爲等待某個事件發生而沒法執行時,便放棄處理機而處於阻塞狀態。引發進程阻塞的事件可有多種,例如,等待I/O完成、申請緩衝區不能知足、等待信件(信號)等。
process模塊是一個建立進程的模塊,藉助這個模塊,就能夠完成進程的建立。
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字符的字符串。這個鍵的用途是爲涉及網絡鏈接的底層進程間通訊提供安全性
,這類鏈接只有在具備相同的身份驗證鍵時才能成功(瞭解便可)
在Windows操做系統中因爲沒有fork(linux操做系統中建立進程的機制),在建立子進程的時候會自動 import 啓動它的這個文件,而在 import 的時
候又執行了整個文件。所以若是將process()直接寫在文件中就會無限遞歸建立子進程報錯。因此必須把建立子進程的部分使用if __name__ ==‘__main__’ 判斷
保護起來,import 的時候 ,就不會遞歸運行了。
multiprocessing模塊建立進程的方式1:
from multiprocessing import Process import time def test(name): print('你是叫%s是吧'%name) print("這是子進程") if __name__ == '__main__': p=Process(target=test,args=('xiaohong',)) #建立一個進程對象 p.start() #告訴操做系統幫你建立一個進程,至於何時建立,怎麼創,由操做系統隨機決定 #time.sleep(0.1) print('我是....') ''' 爲什麼先運行start下面的print,再運行子進程? 由於在運行到start()時,會在內存中申請一塊空間,而後運行.py文件,將代碼丟到內存空間裏面,這時子進程才建立好, 這時建立進程的時間遠遠大於執行start下面print代碼的時間,顧先打印「我是子進程」 進程與進程之間數據是隔離的,沒法直接交互,能夠經過某些技術實現間接交互。 若是在start下方加上睡眠時間,則子進程先啓動,0.1秒的時間足夠操做系統建立子進程,讓CPU讀取子進程中動態運行的代碼 ========= 我是.... 你是叫xiaohong是吧 這是子進程
multiprocessing模塊建立進程的方式2:
'''
自定義的類體中必需要有run方法,才能建立出來一個子進程,其內部函數體代碼才能運行,其餘函數則運行不了
'''
import time from multiprocessing import Process class MyProcess(Process): def __init__(self,name): super().__init__() self.name=name def run(self): #必須是run方法,否則建立不了子進程,其餘方法就只能是一個靜態實體,並不能運行 print('%s is running'%self.name) time.sleep(3) print('%s is overing'%self.name) if __name__ == '__main__': p=MyProcess('zhenhua') p.start() print('一個py文件就是一個主進程') ===== 一個py文件就是一個主進程 zhenhua is running zhenhua is overing
進程的join方法:p.join()可使主進程處於等待狀態,待子進程運行完畢,再啓動主進程。
進程中的join方法1:
在這裏首先說明一下,Process在同時建立多個子進程的時候,同時各啓動子進程,各子進程、主進程是各自隔離運行的,數據不共享,運行也互不干擾,至於操做系統先建立哪一個子進程,由操做系統決定,因此不是按照咱們書寫代碼的順序去建立
from multiprocessing import Process import time def test(name): print('%s is running'%name) time.sleep(2) print('%s is overing'%name) if __name__ == '__main__': p=Process(target=test,args=('king',)) p1=Process(target=test,args=('queen',)) p2=Process(target=test,args=('hero',)) p.start() p1.start() p2.start() #CPU讀取的速度肯快,操做系統至關於同時受到三個建立命令,三個子進程的建立順序及建立時間是操做系統隨機決定的, p.join() #join 可讓主進程暫停運行,等待子進程運行結束才繼續運行 #p.join() print('啥時間運行我呢?') ====== queen is running king is running hero is running queen is overing king is overing hero is overing 啥時間運行我呢?
進程中的join方法2:
from multiprocessing import Process import time def test(name,i): print('%s is running'%name) time.sleep(2) print('%s is overing'%name) if __name__ == '__main__': for i in range(3): p=Process(target=test,args=('進程%s'%i,i)) p.start() p.join() #join 可讓主進程暫停運行,等待子進程運行結束才繼續運行 print('啥時間運行我呢?') ======== 進程0 is running 進程0 is overing 啥時間運行我呢? 進程1 is running 進程1 is overing 啥時間運行我呢? 進程2 is running 進程2 is overing 啥時間運行我呢?
進程中的join方法3:
from multiprocessing import Process import time def test(name,i): print('%s is runing'%name) time.sleep(i) print('%s is overing'%name) if __name__ == '__main__': start = time.time() p_list=[] for i in range(1,4): p=Process(target=test,args=('子進程%s'%i,i )) p_list.append(p) p.start() for p in p_list: p.join() print('主', (time.time() - start)) ===== 子進程3 is runing 子進程1 is runing 子進程2 is runing 子進程1 is overing 子進程2 is overing 子進程3 is overing 主 3.1881821155548096
各進程之間啓動是隔離的
from multiprocessing import Process ''' ''' money=100 def test(): global money money=888 if __name__ == '__main__': p=Process(target=test) p.start() p.join() print(money) ''' 經過添加join讓子進程先運行結束,再繼續運行父進程,發現money的值還 是100,global並無修改父進程中的money,而是修改本身內存中的money ''' ===== 100
from multiprocessing import Process,current_process import time import os def func(i): print(f'子進程{i}的父進程進程號:{os.getppid()},子進程{i}的進程號是:{current_process().pid}') time.sleep(2) if __name__ == '__main__': for i in range(1,5): p = Process(target=func,args=(i,)) p.start() # p.join() # p.terminate() # 截斷進程,其上不能再用join()方法,這樣已經達到讓子進程先運行的效果了,就沒法用terminate阻斷子進程了。 # print(p.is_alive()) print(f'主進程進程號是:{current_process().pid},主進程的父進程是{os.getppid()}') ''' 主進程進程號是:6964,主進程的父進程是2944 子進程2的父進程進程號:6964,子進程2的進程號是:5872 子進程3的父進程進程號:6964,子進程3的進程號是:6772 子進程4的父進程進程號:6964,子進程4的進程號是:1836 子進程1的父進程進程號:6964,子進程1的進程號是:6656 ''' 說明:進程的建立須要由操做系統來決定,建立進程須要須要從新開闢內存空間,把要運行的代碼複製進去,
把運行過程當中產生的名字放到名稱空間,消耗資源;每開闢一個進程,就要開闢一塊新的內存空間,開闢進
程的的所需時間遠遠大於主進程(一個.py文件中的代碼)代碼運行時間,因此,永遠先運行的都是主進程
中的代碼,除非使用join或者設置睡眠時間;建立的歌子進程之間以及主進程之間都是隔離的,互補交涉,
因此是併發運行的,運行時間2s多一點。
進程對象及其餘方法:
current_process.pid()查看當前(子/主)進程號
os.getpid()查看當前(子/主)進程號
os.getppid()查看當前進程的上一級進程號
from multiprocessing import Process,current_process import time import os def func(i): print(f'子進程{i}的父進程進程號:{os.getppid()},子進程{i}的進程號是:{current_process().pid}') time.sleep(60) if __name__ == '__main__': for i in range(1,5): p = Process(target=func,args=(i,)) p.start() print(f'主進程進程號是:{current_process().pid},主進程的父進程是{os.getppid()}') ''' 主進程進程號是:5816,主進程的父進程是2944 子進程3的父進程進程號:5816,子進程3的進程號是:4020 子進程4的父進程進程號:5816,子進程4的進程號是:5204 子進程1的父進程進程號:5816,子進程1的進程號是:6148 子進程2的父進程進程號:5816,子進程2的進程號是:6316 ''' ''' 無論是主進程仍是子進程,或是子進程的父進程的進程號都是運行Python解釋器(進程),主進程(Python解釋器)的父進程是pycharm
殭屍進程
子進程是由主進程建立的,主進程先死,子進程死了以後所佔用的PID及其餘資源沒有被主進程回收,這個子進程就步入了殭屍進程;全部的進程最終都會步入到殭屍進程。
殭屍進程帶來的缺點:操做系統所給的進程號是有限的,若是殭屍進程過多,將致使過多的PID被佔用(沒有回收),致使後期再啓程序會受到阻礙。
父進程回收子進程資源的兩種方式:
1)使用join方法,讓子進程先死,這樣主進程就能夠回收子進程的進程號;據說調用一個叫wait的方法。
2)父進程正常死亡(父進程等子進程死亡本身也死了)
孤兒進程:子進程沒死,父進程意外死亡;
針對Linux會有兒童福利院(init),若是父進程意外死亡,他們所建立的子進程都會被福利院收養。孤兒進程是無害的,由於他的進程號會被福利院回收。
from multiprocessing import Process,current_process import os import time def test(name): print('%s is running'%name,current_process().pid,'父進程PID:%s'%os.getppid()) #查看子進程號 time.sleep(3) print('%s is overing'%name) if __name__ == '__main__': p=Process(target=test,args=('egon',)) #args括號內逗號必需要加上,否則會報錯 p.start() ''' p.terminate() #殺死進程,告訴操做系統,至於操做系統何時殺由他決定, 可是代碼執行的速度比操做系統反應快,因此頗有可能,在進程還沒殺死以前代碼運行結束, is_live的返回值是True,咱們能夠經過在terminate以後加上time.sleep()稍微睡眠一下, ''' p.terminate() time.sleep(0.1) print(p.is_alive() ) #判斷進程是否被殺死,返回的是bool值TRUE、False print('主進程號:%s'%current_process().pid,'主主進程PID:%s'%os.getppid()) #查看主進程號
========
False
主進程號:7332 主主進程PID:3924
守護進程:其本質就是一個子進程「」,該子進程的生命週期<=被守護進程的生命週期,只要主進程一旦結束,子進程不管有沒有結束,都要跟着一塊兒死。
#守護進程: 本質就是一個"子進程",該"子進程"的生命週期<=被守護進程的生命週期 from multiprocessing import Process import time def task(name): print('老太監%s活着....' %name) time.sleep(3) print('老太監%s正常死亡....' %name) if __name__ == '__main__': p=Process(target=task,args=('劉清政',)) p.daemon=True #守護進程要在start以前設置,告訴操做系統該進程是守護進程;在start以後設置就會報錯 p.start() time.sleep(1) print('皇上:EGON正在死...') ======= 老太監劉清政活着.... 皇上:EGON正在死...
互斥鎖:
當多個進程操做同一份數據的時候 會形成數據的錯亂,這個時候必須加鎖處理,這樣將併發變成串行,雖然下降了效率可是提升了數據的安全,
注意:
1.鎖不要輕易使用 容易形成死鎖現象
2.只在處理數據的部分加鎖 不要在全局加鎖
3.鎖必須在主進程中產生 交給子進程去使用
如下依搶票爲例:
進程之間數據不共享,可是共享同一套文件系統,因此訪問同一個文件,或同一個打印終端,是沒有問題的,
而共享帶來的是競爭,競爭帶來的結果就是錯亂,如何控制,就是加鎖處理
'''
0.000幾秒for循環就已經已經結束了,此時操做系統或許連一個進程都尚未建立,當第一個進程建立好以後
運行search函數,在睡眠一秒的時候,全部的進程都起來了,也都在這裏睡眠,第一我的查看到餘票還有1張,別的其餘人查看餘票也是1張
以後都到了買票的環節,此時票數是1,你們都對字典的一進行修改,致使全部人都買票成功,但實際只有一張票
這裏使用互斥鎖,將這種併發問題變成串行來解決,在一我的在作操做的時候其餘人都不能作操做,其餘人作操做是基於前一我的作完操做以後再繼續
互斥鎖把併發(併發效率高於串行)變成串行,犧牲效率,但保證數據安全了
方法一:使用join,這種方法,一次只能啓動一個進程,只有進程1啓動了,進程1 搶票成功,這樣數據安全了,可是永遠只有進程1
搶票成功,也是不合理的。
方法二:導入lock
'''
import time ,json,random from multiprocessing import Process,Lock def search(name): with open(r'xxx.json','rt',encoding='utf-8')as f: dic=json.load(f) time.sleep(1) print('%s查詢餘票爲%s'%(name,dic['count'])) def buy(name): with open(r'xxx.json','rt',encoding='utf_8') as f: dic=json.load(f) if dic['count']>0: dic['count']-=1 time.sleep(random.randint(1, 2)) with open(r'xxx.json', 'wt', encoding='utf-8')as f: json.dump(dic, f) print('%s購票成功' % name) else: print('餘票已經沒了!') def common(name,mutex): search(name)#這裏應該變成併發,枷鎖以後,好多人都阻塞到這裏了 mutex.acquire() #獲取鎖,且同一時間只有一人得到鎖 buy(name) #這裏應該變成串行 mutex.release() #釋放鎖,接下來的人繼續搶鎖 if __name__ == '__main__': mutex=Lock() for i in range(10): p=Process(target=common,args=('路人%s'%i,mutex)) p.start() # p.join() ====== 路人3查詢餘票爲5 路人1查詢餘票爲5 路人0查詢餘票爲5 路人2查詢餘票爲5 路人7查詢餘票爲5 路人4查詢餘票爲5 路人9查詢餘票爲5 路人5查詢餘票爲5 路人6查詢餘票爲5 路人8查詢餘票爲5 路人3購票成功 路人1購票成功 路人0購票成功 路人2購票成功 路人7購票成功 餘票已經沒了! 餘票已經沒了! 餘票已經沒了! 餘票已經沒了! 餘票已經沒了!
#加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,即串行的修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。 雖然能夠用文件共享數據實現進程間通訊,但問題是: 1.效率低(共享數據基於文件,而文件是硬盤上的數據) 2.須要本身加鎖處理 #所以咱們最好找尋一種解決方案可以兼顧:一、效率高(多個進程共享一塊內存的數據)二、幫咱們處理好鎖問題。這就是mutiprocessing模塊爲咱們提供的基於消息的IPC通訊機制:隊列和管道。 1 隊列和管道都是將數據存放於內存中 2 隊列又是基於(管道+鎖)實現的,可讓咱們從複雜的鎖問題中解脫出來, 咱們應該儘可能避免使用共享數據,儘量使用消息傳遞和隊列,避免處理複雜的同步和鎖問題,並且在進程數目增多時,每每能夠得到更好的可獲展性。