1. 程式人生 > >day030程序的兩種建立方法,驗證程序的空間隔離,join等待子程序

day030程序的兩種建立方法,驗證程序的空間隔離,join等待子程序

本節內容:

1.作業系統的簡單介紹
2.程序的兩種建立方法
3.程序之間是空間隔離的,

參考文章:

一、作業系統的簡單介紹

1、作業系統簡單介紹

作業系統就是一個協調、管理和控制計算機硬體資源和軟體資源的控制程式。
作業系統位於計算機硬體與應用軟體之間,本質也是一個軟體。
作業系統由作業系統的核心(運行於核心態,管理硬體資源)以及系統呼叫
(運行於使用者態,為應用程式設計師寫的應用程式提供系統呼叫介面)兩部分組成,
所以,單純的說作業系統是運行於核心態的,是不準確的。

2、細說的話,作業系統應該分成兩部分功能:

一:隱藏了醜陋的硬體呼叫介面(鍵盤、滑鼠、音箱等等怎麼實現的,就不需要你管了),
為應用程式設計師提供呼叫硬體資源的更好,更簡單,更清晰的模型(系統呼叫介面)。
應用程式設計師有了這些介面後,就不用再考慮操作硬體的細節,專心開發自己的應用程式即可。

例如:作業系統提供了檔案這個抽象概念,對檔案的操作就是對磁碟的操作,
有了檔案我們無需再去考慮關於磁碟的讀寫控制(比如控制磁碟轉動,移動磁頭讀寫資料等細節),

二:將應用程式對硬體資源的競態請求變得有序化

例如:很多應用軟體其實是共享一套計算機硬體,比方說有可能有三個應用程式同時需要申請印表機來輸出內容,
那麼a程式競爭到了印表機資源就列印,然後可能是b競爭到印表機資源,也可能是c,這就導致了無序,
印表機可能列印一段a的內容然後又去列印c...,作業系統的一個功能就是將這種無序變得有序。

<details>
<summary>作業系統詳解</summary>

#作用一:為應用程式提供如何使用硬體資源的抽象
例如:作業系統提供了檔案這個抽象概念,對檔案的操作就是對磁碟的操作,有了檔案我們無需再去考慮關於磁碟的讀寫控制

注意:
    作業系統提供給應用程式的該抽象是簡單,清晰,優雅的。為何要提供該抽象呢?
    硬體廠商需要為作業系統提供自己硬體的驅動程式(裝置驅動,這也是為何我們要使用音效卡,就必須安裝音效卡驅動。。。),廠商為了節省成本或者相容舊的硬體,它們的驅動程式是複雜且醜陋的
    作業系統就是為了隱藏這些醜陋的資訊,從而為使用者提供更好的介面
    這樣使用者使用的shell,Gnome,KDE看到的是不同的介面,但其實都使用了同一套由linux系統提供的抽象介面


#作用二:管理硬體資源
    現代的作業系統運行同時執行多道程式,作業系統的任務是在相互競爭的程式之間有序地控制對處理器、儲存器以及其他I/O介面裝置的分配。
例如:
    同一臺計算機上同時執行三個程式,它們三個想在同一時刻在同一臺計算機上輸出結果,那麼開始的幾行可能是程式1的輸出,
    接著幾行是程式2的輸出,然後又是程式3的輸出,最終將是一團糟(程式之間是一種互相競爭資源的過程) 作業系統將印表機的結果送到磁碟的緩衝區,在一個程式完全結束後,才將暫存在磁碟上的檔案送到印表機輸出, 同時其他的程式可以繼續產生更多的輸出結果(這些程式的輸出沒有真正的送到印表機),這樣,作業系統就將由競爭產生的無序變得有序化。

</details>

3、多道程式設計技術(重點)

所謂多道程式設計技術,就是指允許多個程式同時進入記憶體並執行。
即同時把多個程式放入記憶體,並允許它們交替在CPU中執行,它們共享系統中的各種硬、軟體資源。
當一道程式因I/O請求而暫停執行時,CPU便立即轉去執行另一道程式。

1.什麼是io切

即在執行input或者ouput的時候,cup會空閒,
在A程式計算時,I/O空閒, A程式I/O操作時,CPU空閒(B程式也是同樣);
必須A工作完成後,B才能進入記憶體中開始工作,兩者是序列的,全部完成共需時間=T1+T2。

    將A、B兩道程式同時存放在記憶體中,它們在系統的控制下,可相互穿插、交替地在CPU上執行:
當A程式因請求I/O操作而放棄CPU時,B程式就可佔用CPU執行,這樣 CPU不再空閒,而正進行A I/O操作的I/O裝置也不空閒,
顯然,CPU和I/O裝置都處於“忙”狀態,大大提高了資源的利用率,從而也提高了系統的效率,A、B全部完成所需時間<<T1+T2。

    多道程式設計技術不僅使CPU得到充分利用,同時改善I/O裝置和記憶體的利用率,
從而提高了整個系統的資源利用率和系統吞吐量(單位時間內處理作業(程式)的個數),最終提高了整個系統的效率。

單處理機系統中多道程式執行時的特點:
    (1)多道:計算機記憶體中同時存放幾道相互獨立的程式;
    (2)巨集觀上並行:同時進入系統的幾道程式都處於執行過程中,即它們先後開始了各自的執行,但都未執行完畢;
    (3)微觀上序列:實際上,各道程式輪流地用CPU,並交替執行。

2.空間和時間上的複用

多道程式系統的出現,標誌著作業系統漸趨成熟的階段,先後出現了作業排程管理、處理機管理、儲存器管理、外部裝置管理、檔案系統管理等功能。

    由於多個程式同時在計算機中執行,開始有了空間隔離的概念,只有記憶體空間的隔離,才能讓資料更加安全、穩定。
    出了空間隔離之外,多道技術還第一次體現了時空複用的特點,遇到IO操作就切換程式,使得cpu的利用率提高了,計算機的工作效率也隨之提高。

 空間上的複用:將記憶體分為幾部分,每個部分放入一個程式,這樣,同一時間記憶體中就有了多道程式。

 時間上的複用:當一個程式在等待I/O時,另一個程式可以使用cpu,如果記憶體中可以同時存放足夠多的作業,則cpu的利用率可以接近100%

二、什麼是程序

程序(Process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。
在早期面向程序設計的計算機結構中,程序是程式的基本執行實體;在當代面向執行緒設計的計算機結構中,程序是執行緒的容器。
程式是指令、資料及其組織形式的描述,程序是程式的實體。我們自己在python檔案中寫了一些程式碼,這叫做程式,執行這個python檔案的時候,這叫做程序。

  狹義定義:程序是正在執行的程式的例項(an instance of a computer program that is being executed)。
  廣義定義:程序是一個具有一定獨立功能的程式關於某個資料集合的一次執行活動。它是作業系統動態執行的基本單元,在傳統的作業系統中,程序既是基本的分配單元,也是基本的執行單元。

  舉例: 比如py1檔案中有個變數a=1,py2檔案中有個變數a=2,他們兩個會衝突嗎?不會的,是不是,因為兩個檔案執行起來後是兩個程序,作業系統讓他們在記憶體上隔離開,對吧。

<details>
<summary>程序的概念</summary>

第一,程序是一個實體。每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text region)(python的檔案)、資料區域(data region)
(python檔案中定義的一些變數資料)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;
堆疊區域儲存著活動過程呼叫的指令和本地變數。

第二,程序是一個“執行中的程式”。程式是一個沒有生命的實體,只有處理器賦予程式生命時(作業系統執行之),它才能成為一個活動的實體,我們稱其為程序。
程序是作業系統中最基本、重要的概念。

是多道程式系統出現後,為了刻畫系統內部出現的動態情況,
描述系統內部各道程式的活動規律引進的一個概念,所有多道程式設計作業系統都建立在程序的基礎上。

</details>

<details>
<summary>程序的特徵</summary>

動態性:程序的實質是程式在多道程式系統中的一次執行過程,程序是動態產生,動態消亡的。
併發性:任何程序都可以同其他程序一起併發執行
獨立性:程序是一個能獨立執行的基本單位,同時也是系統分配資源和排程的獨立單位;
非同步性:由於程序間的相互制約,使程序具有執行的間斷性,即程序按各自獨立的、不可預知的速度向前推進
結構特徵:程序由程式、資料和程序控制塊三部分組成。
多個不同的程序可以包含相同的程式:一個程式在不同的資料集裡就構成不同的程序,能得到不同的結果;但是執行過程中,程式不能發生改變。

</details>

<details>
<summary>程序與程式的區別</summary>

程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。
而程序是程式在處理機上的一次執行過程,它是一個動態的概念。
程式可以作為一種軟體資料長期存在,而程序是有一定生命期的。
程式是永久的,程序是暫時的。
舉例:就像qq一樣,qq是我們安裝在自己電腦上的客戶端程式,其實就是一堆的程式碼檔案,我們不執行qq,那麼他就是一堆程式碼程式,當我們執行qq的時候,這些程式碼執行起來,就成為一個程序了。

</details>

三、併發與並行

通過程序之間的排程,也就是程序之間的切換,我們使用者感知到的好像是兩個視訊檔案同時在播放,
或者音樂和遊戲同時在進行,那就讓我們來看一下什麼叫做併發和並行

無論是並行還是併發,在使用者看來都是'同時'執行的,不管是程序還是執行緒,都只是一個任務而已,
真是幹活的是cpu,cpu來做這些任務,而一個cpu同一時刻只能執行一個任務

1、併發

是偽並行,即看起來是同時執行。單個cpu+多道技術就可以實現併發,(並行也屬於併發)

fe: 併發的示例說法

你是一個cpu,你同時談了三個女朋友,每一個都可以是一個戀愛任務,你被這三個任務共享要玩出併發戀愛的效果,
應該是你先跟女友1去看電影,看了一會說:不好,我要拉肚子,然後跑去跟第二個女友吃飯,吃了一會說:那啥,我去趟洗手間,然後跑去跟女友3開了個房,
然後在你的基友眼裡,你就在和三個女友同時在一起玩。

2、並行:

並行:同時執行,只有具備多個cpu才能實現並行
將多個cpu必須成高速公路上的多個車道,程序就好比每個車道上行駛的車輛,
並行就是說,大家在自己的車道上行駛,會不影響,同時在開車。這就是並行
單核下,可以利用多道技術,多個核,每個核也都可以利用多道技術(多道技術是針對單核而言的)

  有四個核,六個任務,這樣同一時間有四個任務被執行,假設分別被分配給了cpu1,cpu2,cpu3,cpu4,

    一旦任務1遇到I/O就被迫中斷執行,此時任務5就拿到cpu1的時間片去執行,這就是單核下的多道技術

    而一旦任務1的I/O結束了,作業系統會重新呼叫它(需知程序的排程、分配給哪個cpu執行,由作業系統說了算),可能被分配給四個cpu中的任意一個去執行 所有現代計算機經常會在同一時間做很多件事,一個使用者的PC(無論是單cpu還是多cpu),都可以同時執行多個任務(一個任務可以理解為一個程序)。     啟動一個程序來防毒(360軟體)     啟動一個程序來看電影(暴風影音)     啟動一個程序來聊天(騰訊QQ)   所有的這些程序都需被管理,於是一個支援多程序的多道程式系統是至關重要的 多道技術概念回顧:記憶體中同時存入多道(多個)程式,cpu從一個程序快速切換到另外一個,使每個程序各自執行幾十或幾百毫秒,這樣, 雖然在某一個瞬間,一個cpu只能執行一個任務,但在1秒內,cpu卻可以執行多個程序, 這就給人產生了並行的錯覺,即偽並行,以此來區分多處理器作業系統的真正硬體並行(多個cpu共享同一個實體記憶體)

四、同步\非同步\阻塞\非阻塞(重點)

1.程序狀態介紹

在瞭解其他概念之前,我們首先要了解程序的幾個狀態。
在程式執行的過程中,由於被作業系統的排程演算法控制,程式會進入幾個狀態:就緒,執行和阻塞。

  (1)就緒(Ready)狀態

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

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

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

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

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

  時間片到了之後有回到就緒狀態,這三個狀態不斷的在轉換。

2、同步非同步

所謂同步
就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列。
要麼成功都成功,失敗都失敗,兩個任務的狀態可以保持一致。
其實就是一個程式結束才執行另外一個程式,序列的,不一定兩個程式就有依賴關係。

所謂非同步
是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工作,依賴的任務也立即執行,
只要自己完成了整個任務就算完成了。
至於被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列。

fe:例項說明

同步:你跟你前面的點了一份豬腳飯,只有一個廚師,需要排你前面的人完成了,才到你,
非同步:取號排隊,取了號,你可以在期間做其他事情,去喝個茶,打個遊戲,逛個街再回來

3、阻塞與非阻塞

阻塞和非阻塞這兩個概念與程式(執行緒)等待訊息通知(無所謂同步或者非同步)時的狀態有關。
也就是說阻塞與非阻塞主要是程式(執行緒)等待訊息通知時的狀態角度來說的

fe:例項說明

阻塞:在排隊的時候,什麼都沒帶,只能乾等這排隊,做不了其他事情
非阻塞:在排隊的時候,帶了手機,可以邊排隊邊打遊戲

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),每個程序佔用一個程序表項(這些表項也稱為程序控制塊)

該表存放了程序狀態的重要資訊:程式計數器、堆疊指標、記憶體分配狀況、所有開啟檔案的狀態、帳號和排程資訊,以及其他在程序由執行態轉為就緒態或阻塞態時,必須儲存的資訊,從而保證該程序在再次啟動時,就像從未被中斷過一樣。

fe1:程序建立示例

from multiprocessing import Process
# 方式1
import time

def func1(): time.sleep(3) print(111111) def func2(): time.sleep(2) print(22222) print('子程序的pid',os.getpid()) #檢視當前程序的id,這個不是埠號 print('子程序的父程序>>>',os.getppid()) #檢視父程序的id if __name__ == '__main__': print('主程序的pid',os.getpid()) print('當前主程序的父程序>>>',os.getppid()) p1 = Process(target=func1) p1.start() p2 = Process(target=func2) p2.start() #告訴作業系統,我這邊準備好了,你幫我建立並執行這個程序 p1.join() #等待p1子程序執行完,主程序從這裡才繼續執行 p2.join() # time.sleep(2) print('主程序')

fe2:程序空間隔離示例

from multiprocessing import Process

num = 20

def func(): global num num = 10 # 這裡修改不會影響到主程序的num,體現了相互隔離 print('子程序的num>>>>',num) # 這裡的num是在子程序有自己的記憶體空間,跟主程序的空間隔離了 if __name__ == '__main__': p = Process(target=func,) p.start() p.join() print('主程序num:',num)

六、程序物件的其他方法一:terminate, is_alive

terminate()# 關閉程序,不會立即關閉,有個等著作業系統去關閉這個程序的時間,所以is_alive立刻檢視的結果可能還是存活,但是稍微等一會,就被關掉了
is_alive() # 判斷程序是否還在存活,返回結果為:True或False

fe: 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) time.sleep(2) # s = input('???') #別忘了再pycharm下子程序中不能input輸入,會報錯EOFError: EOF when reading a line,因為子程序中沒有像我們主程序這樣的在pycharm下的控制檯可以輸入東西的地方 print('%s is 修飛機結束' %self.name) if __name__ == '__main__': p1=Piao('太白') p1.start() p1.terminate()#關閉程序,不會立即關閉,有個等著作業系統去關閉這個程序的時間,所以is_alive立刻檢視的結果可能還是存活,但是稍微等一會,就被關掉了 print(p1.is_alive()) #結果為True print('等會。。。。') time.sleep(1) print(p1.is_alive()) #結果為False

fe:pip name

from multiprocessing import Process
import time
import random
import os class Piao(Process): def __init__(self): # self.name=name # super().__init__() #Process的__init__方法會執行self.name=Piao-1, # #所以加到這裡,會覆蓋我們的self.name=name #為我們開啟的程序設定名字的做法 super().__init__() # self.name=name def run(self): # print('子程序id',os.getpid()) print('%s is piaoing' %self.name) # 程序的名字 time.sleep(random.randrange(1,3)) print('%s is piao end' %self.name) if __name__ == '__main__': p=Piao() p.start() print('開始') print(p.pid) #檢視pid print(p.name) #檢視pid print(os.getpid())