1. 程式人生 > >Python基礎30_程序

Python基礎30_程序

一. 背景知識 程序就是進行中的程式 1. 程序的概念起源於作業系統, 是作業系統最核心的概念, 作業系統的其他所有內容都是圍繞程序的概念展開的 作業系統的作用: (1). 隱藏醜陋複雜的硬體介面, 提供良好的抽象介面 (2).管理,排程程序, 並且將多個程序對硬體的競爭變得有序 2. 多道技術: 產生背景: 針對單核, 實現併發 記憶體中同時存入多道(多個)程式,cpu從一個程序快速切換到另外一個,使每個程序各自執行幾十或幾百毫秒,這樣,雖然在某一個瞬間,一個cpu只能執行一個任務,但在1秒內,cpu卻可以執行多個程序,這就給人產生了並行的錯覺,即偽並行,以此來區分多處理器作業系統的真正硬體並行(多個cpu共享同一個實體記憶體) 現在的主機一般都是多核, 那麼每個核都會利用多道技術 空間上的複用: 將記憶體分為幾個部分, 每個部分放入一個程式, 這樣, 同一時間記憶體中就有了多道程式 時間上的複用: 當一個程式等待I/O 時, 另一個程式會使用cpu 遇到I/O切, 佔用cpu時間過長也切, 核心在於切之前將程序的狀態儲存下來, 這樣才能保證下次切回來時, 能基於上次切走的位置繼續執行 二. 什麼是程序 Process 1. 概念 程序是計算機中的程式關於某資料集合上的一次執行活動, 是系統進行資源分配和排程的基本單位, 是作業系統機構的基礎. 在早期面向程序設計的計算機結構中, 程序是程式的基本執行實體, 在當代面向程序設計的計算機結構中, 程序是執行緒的容器, 程式時指令, 資料及其組織形式的描述, 程序是程式的實體, 我們自己在python檔案中寫了一些程式碼, 這叫做程式, 執行這個python檔案的時候, 叫程序. 第一,程序是一個實體。每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text region)(python的檔案)、資料區域(data region)(python檔案中定義的一些變數資料)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;堆疊區域儲存著活動過程呼叫的指令和本地變數。 第二,程序是一個“執行中的程式”。程式是一個沒有生命的實體,只有處理器賦予程式生命時(作業系統執行之),它才能成為一個活動的實體,我們稱其為程序。 2. 特徵: 動態性:程序的實質是程式在多道程式系統中的一次執行過程,程序是動態產生,動態消亡的。 併發性:任何程序都可以同其他程序一起併發執行 獨立性:程序是一個能獨立執行的基本單位,同時也是系統分配資源和排程的獨立單位; 非同步性:由於程序間的相互制約,使程序具有執行的間斷性,即程序按各自獨立的、不可預知的速度向前推進 結構特徵:程序由程式、資料和程序控制塊三部分組成。 多個不同的程序可以包含相同的程式:一個程式在不同的資料集裡就構成不同的程序,能得到不同的結果;但是執行過程中,程式不能發生改變。 3. 程序和程式的區別: 程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。 而程序是程式在處理機上的一次執行過程,它是一個動態的概念。 程式可以作為一種軟體資料長期存在,而程序是有一定生命期的。 程式是永久的,程序是暫時的。 舉例:就像qq一樣,qq是我們安裝在自己電腦上的客戶端程式,其實就是一堆的程式碼檔案,我們不執行qq,那麼他就是一堆程式碼程式,當我們執行qq的時候,這些程式碼執行起來,就成為一個程序了。 4. 注意: 同一個程式執行功能兩次, 就會在作業系統中出現兩個程序, 所以我們可以同時執行一個軟體, 分別做不同的事情也不會混亂. 三. 程序排程: 要想多個程序交替執行,作業系統必須對這些程序進行排程,這個排程也不是隨即進行的,而是需要遵循一定的法則,由此就有了程序的排程演算法。 1. 先來先服務(FCFS)排程演算法是一種最簡單的排程演算法,該演算法既可用於作業排程,也可用於程序排程。FCFS演算法比較有利於長作業(程序),而不利於短作業(程序)。由此可知,本演算法適合於CPU繁忙型作業,而不利於I/O繁忙型的作業(程序)。 2. 短作業(程序)優先排程演算法(SJ/PF)是指對短作業或短程序優先排程的演算法,該演算法既可用於作業排程,也可用於程序排程。但其對長作業不利;不能保證緊迫性作業(程序)被及時處理;作業的長短只是被估算出來的。 3. 時間片輪轉(Round Robin,RR)法的基本思路是讓每個程序在就緒佇列中的等待時間與享受服務的時間成比例。在時間片輪轉法中,需要將CPU的處理時間分成固定大小的時間片,例如,幾十毫秒至幾百毫秒。如果一個程序在被排程選中之後用完了系統規定的時間片,但又未完成要求的任務,則它自行釋放自己所佔有的CPU而排到就緒佇列的末尾,等待下一次排程。同時,程序排程程式又去排程當前就緒佇列中的第一個程序。 顯然,輪轉法只能用來排程分配一些可以搶佔的資源。這些可以搶佔的資源可以隨時被剝奪,而且可以將它們再分配給別的程序。CPU是可搶佔資源的一種。但印表機等資源是不可搶佔的。由於作業排程是對除了CPU之外的所有系統硬體資源的分配,其中包含有不可搶佔資源,所以作業排程不使用輪轉法。 在輪轉法中,時間片長度的選取非常重要。首先,時間片長度的選擇會直接影響到系統的開銷和響應時間。如果時間片長度過短,則排程程式搶佔處理機的次數增多。這將使程序上下文切換次數也大大增加,從而加重系統開銷。反過來,如果時間片長度選擇過長,例如,一個時間片能保證就緒佇列中所需執行時間最長的程序能執行完畢,則輪轉法變成了先來先服務法。時間片長度的選擇是根據系統對響應時間的要求和就緒佇列中所允許最大的程序數來確定的。 在輪轉法中,加入到就緒佇列的程序有3種情況: 一種是分給它的時間片用完,但程序還未完成,回到就緒佇列的末尾等待下次排程去繼續執行。 另一種情況是分給該程序的時間片並未用完,只是因為請求I/O或由於程序的互斥與同步關係而被阻塞。當阻塞解除之後再回到就緒佇列。 第三種情況就是新建立程序進入就緒佇列。 如果對這些程序區別對待,給予不同的優先順序和時間片從直觀上看,可以進一步改善系統服務質量和效率。例如,我們可把就緒佇列按照程序到達就緒佇列的型別和程序被阻塞時的阻塞原因分成不同的就緒佇列,每個佇列按FCFS原則排列,各佇列之間的程序享有不同的優先順序,但同一佇列內優先順序相同。這樣,當一個程序在執行完它的時間片之後,或從睡眠中被喚醒以及被建立之後,將進入不同的就緒佇列。 4. 前面介紹的各種用作程序排程的演算法都有一定的侷限性。如短程序優先的排程演算法,僅照顧了短程序而忽略了長程序,而且如果並未指明程序的長度,則短程序優先和基於程序長度的搶佔式排程演算法都將無法使用。 而多級反饋佇列排程演算法則不必事先知道各種程序所需的執行時間,而且還可以滿足各種型別程序的需要,因而它是目前被公認的一種較好的程序排程演算法。在採用多級反饋佇列排程演算法的系統中,排程演算法的實施過程如下所述。 (1) 應設定多個就緒佇列,併為各個佇列賦予不同的優先順序。第一個佇列的優先順序最高,第二個佇列次之,其餘各佇列的優先權逐個降低。該演算法賦予各個佇列中程序執行時間片的大小也各不相同,在優先權愈高的佇列中,為每個程序所規定的執行時間片就愈小。例如,第二個佇列的時間片要比第一個佇列的時間片長一倍,……,第i+1個佇列的時間片要比第i個佇列的時間片長一倍。 (2) 當一個新程序進入記憶體後,首先將它放入第一佇列的末尾,按FCFS原則排隊等待排程。當輪到該程序執行時,如它能在該時間片內完成,便可準備撤離系統;如果它在一個時間片結束時尚未完成,排程程式便將該程序轉入第二佇列的末尾,再同樣地按FCFS原則等待排程執行;如果它在第二佇列中執行一個時間片後仍未完成,再依次將它放入第三佇列,……,如此下去,當一個長作業(程序)從第一佇列依次降到第n佇列後,在第n 佇列便採取按時間片輪轉的方式執行。 (3) 僅當第一佇列空閒時,排程程式才排程第二佇列中的程序執行;僅當第1~(i-1)佇列均空時,才會排程第i佇列中的程序執行。如果處理機正在第i佇列中為某程序服務時,又有新程序進入優先權較高的佇列(第1~(i-1)中的任何一個佇列),則此時新程序將搶佔正在執行程序的處理機,即由排程程式把正在執行的程序放回到第i佇列的末尾,把處理機分配給新到的高優先權程序。 四. 併發與並行 無論是並行還是併發,在使用者看來都是'同時'執行的,不管是程序還是執行緒,都只是一個任務而已,真是幹活的是cpu,cpu來做這些任務,而一個cpu同一時刻只能執行一個任務 併發: 是偽並行, 即看起來是同時執行, 單個cpu+多道技術就可以實現併發(並行也屬於併發) 並行: 同時執行, 只有具備多個cpu才能實行並行 單核下,可以利用多道技術,多個核,每個核也都可以利用多道技術(多道技術是針對單核而言的) 有四個核,六個任務,這樣同一時間有四個任務被執行,假設分別被分配給了cpu1,cpu2,cpu3,cpu4, 一旦任務1遇到I/O就被迫中斷執行,此時任務5就拿到cpu1的時間片去執行,這就是單核下的多道技術 而一旦任務1的I/O結束了,作業系統會重新呼叫它(需知程序的排程、分配給哪個cpu執行,由作業系統說了算),可能被分配給四個cpu中的任意一個去執行 五. 同步, 非同步, 阻塞, 非阻塞 1. 程序狀態介紹: (1)就緒(Ready)狀態: 當程序已分配到除CPU以外的所有必要的資源,只要獲得處理機便可立即執行,這時的程序狀態稱為就緒狀態。 (2)執行/執行(Running)狀態: 當程序已獲得處理機,其程式正在處理機上執行,此時的程序狀態稱為執行狀態。 (3)阻塞(Blocked)狀態: 正在執行的程序,由於等待某個事件發生而無法執行時,便放棄處理機而處於阻塞狀態。引起程序阻塞的事件可有多種,例如,等待I/O完成、申請緩衝區不能滿足、等待信件(訊號)等。 事件請求:input、sleep、檔案輸入輸出、recv、accept等 事件發生:sleep、input等完成了 時間片到了之後有回到就緒狀態,這三個狀態不斷的在轉換。 2. 同步非同步: (1). 同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列。要麼成功都成功,失敗都失敗,兩個任務的狀態可以保持一致。其實就是一個程式結束才執行另外一個程式,序列的,不一定兩個程式就有依賴關係。 (2). 非同步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工作,依賴的任務也立即執行,只要自己完成了整個任務就算完成了。至於被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列。 比如我們去樓下的老家肉餅吃飯,飯點好了,取餐的時候發生了一些同步非同步的事情。 同步:我們都站在隊裡等著取餐,前面有個人點了一份肉餅,後廚做了很久,但是由於同步機制,我們還是要站在隊裡等著前面那個人的肉餅做好取走,我們才往前走一步。 非同步:我們點完餐之後,點餐員給了我們一個取餐號碼,跟你說,你不用在這裡排隊等著,去找個地方坐著玩手機去吧,等飯做好了,我叫你。這種機制(等待別人通知)就是非同步等待訊息通知。在非同步訊息處理中,等待訊息通知者(在這個例子中等著取餐的你)往往註冊一個回撥機制,在所等待的事件被觸發時由觸發機制(點餐員)通過某種機制(喊號,‘250號你的包子好了‘)找到等待該事件的人。 3. 阻塞和非阻塞: 阻塞和非阻塞這兩個概念與程式(執行緒)等待訊息通知(無所謂同步或者非同步)時的狀態有關。也就是說阻塞與非阻塞主要是程式(執行緒)等待訊息通知時的狀態角度來說的 繼續上面的那個例子,不論是排隊還是使用號碼等待通知,如果在這個等待的過程中,等待者除了等待訊息通知之外不能做其它的事情,那麼該機制就是阻塞的,表現在程式中,也就是該程式一直阻塞在該函式呼叫處不能繼續往下執行。 相反,有的人喜歡在等待取餐的時候一邊打遊戲一邊等待,這樣的狀態就是非阻塞的,因為他(等待者)沒有阻塞在這個訊息通知上,而是一邊做自己的事情一邊等待。阻塞的方法:input、time.sleep,socket中的recv、accept等等。 4.同步/非同步 與 阻塞和非阻塞 (1). 同步阻塞形式 效率最低。拿上面的例子來說,就是你專心排隊,什麼別的事都不做。 (2). 非同步阻塞形式 如果在排隊取餐的人採用的是非同步的方式去等待訊息被觸發(通知),也就是領了一張小紙條,假如在這段時間裡他不能做其它的事情,就在那坐著等著,不能玩遊戲等,那麼很顯然,這個人被阻塞在了這個等待的操作上面; 非同步操作是可以被阻塞住的,只不過它不是在處理訊息時阻塞,而是在等待訊息通知時被阻塞。 (3). 同步非阻塞形式 實際上是效率低下的。 想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有,如果把打電話和觀察排隊的位置看成是程式的兩個操作的話,這個程式需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的。 (4). 非同步非阻塞形式 效率更高, 因為打電話是你(等待者)的事情,而通知你則是櫃檯(訊息觸發機制)的事情,程式沒有在兩種不同的操作中來回切換。 比如說,這個人突然發覺自己煙癮犯了,需要出去抽根菸,於是他告訴點餐員說,排到我這個號碼的時候麻煩到外面通知我一下,那麼他就沒有被阻塞在這個等待的操作上面,自然這個就是非同步+非阻塞的方式了。 很多人會把同步和阻塞混淆,是因為很多時候同步操作會以阻塞的形式表現出來,同樣的,很多人也會把非同步和非阻塞混淆,因為非同步操作一般都不會在真正的IO操作處被阻塞。 六. 程序的建立. 結束.與併發的實現 1. 程序的建立: 對於通用系統(跑很多應用程式),需要有系統執行過程中建立或撤銷程序的能力,主要分為4種形式建立新的程序 (1). 系統初始化(檢視程序linux中用ps命令,windows中用工作管理員,前臺程序負責與使用者互動,後臺執行的程序與使用者無關,執行在後臺並且只在需要時才喚醒的程序,稱為守護程序,如電子郵件、web頁面、新聞、列印) (2). 一個程序在執行過程中開啟了子程序(如nginx開啟多程序,os.fork,subprocess.Popen等) (3). 使用者的互動式請求,而建立一個新程序(如使用者雙擊暴風影音) (4). 一個批處理作業的初始化(只在大型機的批處理系統中應用) 無論哪一種,新程序的建立都是由一個已經存在的程序執行了一個用於建立程序的系統呼叫而建立的: (1). 在UNIX中該系統呼叫是:fork,fork會建立一個與父程序一模一樣的副本,二者有相同的儲存映像、同樣的環境字串和同樣的開啟檔案(在shell直譯器程序中,執行一個命令就會建立一個子程序) (2). 在windows中該系統呼叫是:CreateProcess,CreateProcess既處理程序的建立,也負責把正確的程式裝入新程序。 關於建立的子程序,UNIX和windows (1). 相同的是:程序建立後,父程序和子程序有各自不同的地址空間(多道技術要求物理層面實現程序之間記憶體的隔離),任何一個程序的在其地址空間中的修改都不會影響到另外一個程序。 (2). 不同的是:在UNIX中,子程序的初始地址空間是父程序的一個副本,提示:子程序和父程序是可以有隻讀的共享記憶體區的。但是對於windows系統來說,從一開始父程序與子程序的地址空間就是不同的。 2.程序的結束 (1). 正常退出(自願,如使用者點選互動式頁面的叉號,或程式執行完畢呼叫發起系統呼叫正常退出,在linux中用exit,在windows中用ExitProcess) (2). 出錯退出(自願,python a.py中a.py不存在) (3). 嚴重錯誤(非自願,執行非法指令,如引用不存在的記憶體,1/0等,可以捕捉異常,try...except...) (4). 被其他程序殺死(非自願,如kill -9) 3.程序併發的實現 程序併發的實現在於,硬體中斷一個正在執行的程序,把此時程序執行的所有狀態儲存下來,為此,作業系統維護一張表格,即程序表(process table),每個程序佔用一個程序表項(這些表項也稱為程序控制塊) 該表存放了程序狀態的重要資訊:程式計數器、堆疊指標、記憶體分配狀況、所有開啟檔案的狀態、帳號和排程資訊,以及其他在程序由執行態轉為就緒態或阻塞態時,必須儲存的資訊,從而保證該程序在再次啟動時,就像從未被中斷過一樣。 ---------------------------------------------------------------------------- 通過上面內容的學習,我們已經瞭解了很多程序相關的理論知識,瞭解程序是什麼應該不再困難了,剛剛我們已經瞭解了,執行中的程式就是一個程序。所有的程序都是通過它的父程序來建立的。因此,執行起來的python程式也是一個程序,那麼我們也可以在程式中再建立程序。多個程序可以實現併發效果,也就是說,當我們的程式中存在多個程序的時候,在某些時候,就會讓程式的執行速度變快。以我們之前所學的知識,並不能實現建立程序這個功能,所以我們就需要藉助python中強大的模組。 ---------------------------------------------------------------------------------