先來理解下2個名詞
暫存器:暫存器是中央處理器的一分部 在計算機領域中 暫存器是CPU的內部元件用來暫時存放指令 資料 和地址的地方程式計數器:用來存放下一條指令所在單元地址的地方。當執行一條指令時,首先需要根據PC中存放的指令地址,將指令由記憶體取到指令暫存器中,此過程稱為“取指令”。與此同時,PC中的地址或自動加1或由轉移指標給出下一條指令的地址。此後經過分析指令,執行指令。完成第一條指令的執行,而後根據PC取出第二條指令的地址,如此迴圈,執行每一條指令
程式的概念
在計算機作業系統 程式是對正在執行的程式的抽象
簡單點講 一個正在執行的程式就是一個程式(processes),其中包括了他的暫存器 程式計數器,變數的值 堆疊等等。
比如開啟一個word 開啟一個QQ 開啟一個網易雲音樂 這些都是一個程式
程式模型
計算機中所有可執行的軟體(包括作業系統)被組織成若干順序程式 也就是程式 他使得並行更容易處理(其實是偽並行吧 因為真正的CPU會在程式之間來回切換 來執行程式 只是速度非常快 讓人們感覺不到切換 以為是並行處理的)
比如上面的例子 有4個程式 分別是 A、B、C、D 每個程式都有自己的程式計數器 和自己的處理邏輯 每個都是獨立的 CPU在執行的時候會在他們之間來回的切換 當然最外面的物理計數器只有一個 當CPU執行到程式A的時候回把A的程式計數器給最外面的物理計算器然後執行 當CPU決定要切換到下一個程式的時候 (例子中就是B了)先把當前進行了的計數器重新還給程式的A的程式計數器 然後物理計算器在獲取程式B的程式計數器繼續執行 後面同理 每個程式的處理速度都不一樣 當然也會有優先順序問題 這些就是CPU的切換演算法涉及的一些東西 不是很懂了這個
程式的建立
一般來講會有下面4種情況會建立程式
- 系統初始化
- 一個程式呼叫了建立程式的系統呼叫
- 使用者請求建立一個程式(比如使用者在終端啟動一個編譯器)
- 批處理作業的初始化(這種情況可能我們沒有接觸過,主要就是在大型機的批處理操作會一次提交批處理作業 當作業系統認為有足夠的資源來處理另外一個作業的時候就會啟動一個程式來處理 這種系統我是沒見過 囧~)
程式的終止
一般來講 程式的終止也有4種情況
- 正常退出(自願)–這種情就是程式執行完了自動退出
- 出錯退出(自願)–這種就是程式出現了錯誤 比如
cat xx.file
當 xx.file 不存在的時候直接退出 - 嚴重錯誤(非自願)–這種就是編譯我們的程式碼,程式碼裡有嚴重的錯誤
- 被其他程式殺死(非自願)
程式的狀態
每個程式都是一個獨立的實體。有他自己的程式計數器、堆疊、開啟的檔案、定時器、和內部狀態等。當程式的在執行的時候有下面3中狀態
- 執行態(Running 在這個時候是真正佔用了處理機(比如CPU))
- 就緒態(Ready 可執行,但是因為其他程式在執行他被暫時掛起)
- 阻塞態(Blocked 這種時候除非某種外部事件發生,否則不能執行)
前面2中狀態下 其實都可以執行 只是後者中暫時沒有CPU分配給他.
第三種狀態下不可執行 即使把CPU資源分配給了他也不可執行.
- 上面是一個程式狀態轉換圖 下面來舉例說明一下
- 轉換1由執行態—->阻塞態 這種情況是程式發現自己無法繼續執行下去
比如一個程式需要從終端讀取輸入才可繼續執行 而終端一直沒有輸入 程式會發生這種轉換 - 轉換2和轉換3 是由系統排程器來控制的,系統排程器是作業系統的一分部我們感覺不到(程式自己都感覺不到!!)
當處理機任務當前程式程式佔用的時間已經夠長 決定讓其他程式來執行的時候 會發生轉換2 把當前程式的由執行態轉換成就緒態。
當系統排程器任務其他程式已經佔用了夠多的時間 重新輪換到當前程式來執行的時候他就會發生轉換3 把程式的就緒態轉換成執行態來繼續執行當前的程式 - 當一個程式等待外部時間發生時 發生轉換4 比如到達了一些外部輸入
科普下排程器的只要功能:它主要用來決定當前那個程式可以執行(佔用CPU資源)以及程式可以執行多長時間。現在已經有很多種成熟的演算法來平衡系統的呼叫 這些東西就不是我所理解的範圍了 囧
程式的實現
為了實現程式模型 系統維護著一張表 這個表就是程式表(processes table)每個程式佔用一個表項(個人理解相當於一個資料庫表的一條記錄吧)。這些表項包含了程式的狀態,他的計數器、棧指標、記憶體分配狀況、開啟檔案狀態、統計和排程資訊、定時器和其他訊號 以及他由執行態到就緒態轉換所需要儲存的其他資訊(個人理解這些資訊就相當於資料庫表的欄位 或者屬性一樣的東西吧)。這些資訊是為了讓他再次執行的時候像未中斷過一樣。
在一些系統中程式通訊、記憶體管理、檔案管理是由幾個不同的模組來管理的 然後他們就把程式表給分割開了 也就是他們各自維護了程式表的一些資訊(相當於上面的欄位吧)
下面來舉例說明一臺有單個CPU 多個I/O裝置的計算機如果維護多個順序程式(也就是程式)來做更多的解釋。
假設當一個磁碟發生中斷(其實就是cpu從當前程式切換到別的程式)時 使用者程式(id 23)正在執行,則中斷硬體將當前的程式計數器、程式狀態字以及可能的一個或多個暫存器壓入當前堆疊 計算機立即跳轉到中斷向量多指定的地址處,這些步驟都是硬體做的處理(至於硬體怎麼實現的並不關心估計也是電氣工程學)。後面軟體就接管了後續的操作。中斷服務的過程從全部暫存器壓入堆疊開始,當前“程式號”(這個例子裡就是23)及一個指向其表項的指標被儲存在全域性變數中 這是為了能夠迅速找回他們,隨後將中斷存入的那部分資訊從堆疊中刪除,並將棧指標指向一個被程式處理程式(這裡應該是剛剛23號處理中所佔用的堆疊)所使用的臨時堆疊,然後呼叫一個C過程來處理特定中斷剩下的工作。一般程式間通訊是通過訊息完成的(管道也可以) 下一步構造一條傳送給磁碟程式的訊息 這時磁碟程式正被阻塞等待該訊息的到來。這條訊息說發生了一條中斷,以此來區分別的使用者的讀磁碟訊息(因為現在中斷了但是還會有別的使用者正傳送讀取的操作啊),現在磁碟狀態由阻塞態轉為就緒態然後中斷伺服器呼叫排程器來完成排程 決定是中斷(繼續執行當前程式)還是給別的使用者讀取(也就是別的程式) 下面是一箇中斷處理的大概執行過程 不同的作業系統可能會有所不同
執行緒的概念
- 在傳統作業系統中,每個程式只存在一個地址空間和一個控制流 這個控制流就是執行緒(thread)。有些情況下需要在同一個地址空間中有多條控制流並行的執行 就像他們都是單獨的程式一樣(其實他們只是共享地址空間)這些多個控制流就是–多執行緒
- 程式中有一個存放程式正文和資料以及其他資源的地址空間。這些資源包括開啟的檔案、子程式、未處理的定時器、訊號處理器和審計資訊
- 程式具有另外一個重要的概念就是執行流 — 也就是執行緒
- 執行緒有一個程式計數器 用來跟蹤下一個將要執行的指令
- 執行緒有暫存器 用來儲存當前使用的變數
- 執行緒有堆疊 用來儲存執行的歷史 其中每一棧幀儲存了沒有返回的過程呼叫
儘管執行緒需要在程式中執行,但程式和執行緒是可以分別對待的2個不同的概念,程式是用來程式資源整合而執行緒是CPU排程的實體
這個圖展示了一個在使用者空間 三個程式各有一個執行緒 和 一個程式包含3個執行緒
這個是每個程式和執行緒的一些區別
另外在一些系統中 作業系統是知道每個程式有多少執行緒的,所以當一個執行緒阻塞時 系統會選擇下一個執行緒來執行 這些執行緒可能來自同一個程式 也可以是不同的執行緒。為了進行排程核心必須有一張執行緒表 記錄所有的執行緒 這個就與程式表類似了
CPU線上程之間切換時是有損耗的 因為他需要儲存程式的狀態 暫存器之類的資訊 以便下次執行該程式的時候可以無縫銜接就像未中斷過一樣,雖然是在程式間切換但是程式下面又細分出了執行緒 所以也可以說是線上程之間切換的,這就產生了兩種方式
一種是把執行緒管理放在使用者空間 因為在使用者空間切換速度比核心呼叫的情況速度要快的多(為什麼使用者空間比核心呼叫快呢?這裡還真不是很清楚!!!!)
使用者執行緒和核心執行緒的區別主要表現在效能上。使用者執行緒切換隻需要幾條指令,而核心執行緒的切換需要完成的上下文切換,修改記憶體對映,使快取記憶體失效,這將比使用者執行緒慢了幾個數量級 這裡有篇部落格寫的很詳細
另一種是把執行緒管理放在核心。因為如果把執行緒全部放在使用者空間管理 如果一個執行緒發生了阻塞 則核心將整個程式阻塞 因為核心根本不知道其他執行緒的存在 也就無法排程到其他的執行緒上
無論選擇哪種方式都有一大堆問題需要解決 這裡不是我們要涉及的範圍了 估計又可以搞幾本書。
本作品採用《CC 協議》,轉載必須註明作者和本文連結