操作系统机制学习总结
生活随笔
收集整理的這篇文章主要介紹了
操作系统机制学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
操作系統的運行機制
http://blog.csdn.net/dongyanxia1000/article/details/51244589?1、程序
在計算機系統中,通常CPU執行兩種不同性質的程序:
------- 一種是操作系統內核程序;
------- 另一種是用戶自編程序或系統外層的應用程序。
內核程序是應用程序的”管理者”。
“管理程序“可以執行一些特權指令,而”被管理程序“出于安全考慮不能執行這些指令。
所謂特權指令,是指計算機中不允許用戶直接使用的指令,如:
I/O指令、置中斷指令,存取用于內存保護的寄存器,送程序狀態字到程序狀態字寄存器等指令。
操作系統在具體實現上劃分了用戶態(目態)和核心態(管態),以嚴格區分兩類程序。
2、層次式結構
在軟件工程思想和結構程序設計方法的影響下誕生的現代操作系統,幾乎都是層次式的結構。
操作系統的各項功能分別被設置在不同的層次上。
------- ?一些與硬件關聯較緊密的模塊,諸如時鐘管理、中斷管理、設備驅動等處于最底層。
------- ?其次是運行頻率較高的程序,諸如進程管理、存儲管理和設備管理等。
上面的這兩部分內容構成了操作系統的內核,這部分內容的指令操作工作在核心態。
3、內核
內核是計算機上配置的底層軟件,是計算機功能的延伸。
不同系統對內核的定義稍有區別,大多數操作系統內核包括4個方面的內容。
1)時鐘管理
在計算機的各種部件中,時鐘是最關鍵的設備。
時鐘的第一功能是計時,操作系統需要通過時鐘管理,向用戶提供標準的系統時間。
其次,通過時鐘中斷的管理,可以實現進程的切換。
在分時操作系統中,采用時間片輪轉調度的實現;
在實時系統中,按截至時間控制運行的實現;
在批處理系統中,通過時鐘管理來衡量一個作業的運行程度等。
2)中斷機制
引入中斷技術的初衷是提高多道程序運行環境中CPU的利用率,主要針對外部設備。
后來逐步得到發展,形成了多種類型,稱為操作系統各項操作的基礎。
如,鍵盤或鼠標信息的輸入、進程的管理和調度、系統功能的調用、設備驅動、文件訪問等。都依賴于中斷機制。
可以說,現代操作系統是靠中斷驅動的軟件。
中斷機制中,只有一小部分功能屬于內核,負責保護和恢復中斷現場的信息,轉移控制權到相關的處理程序。
這樣可以減少中斷的處理時間,提高系統的并行處理能力。
3)原語
按照層次結構設計的操作系統,底層必然是一些可被調用的公用小程序,它們各自完成一個規定的操作,其特點是:
------ ?它們處于操作系統的最底層,是最接近硬件的部分。
------ ?這些程序的運行具有原子性---其操作只能一氣呵成
------ ?這些程序的運行時間都較短,而且調用頻繁。
通常把具有這些特點的程序稱為原語(Atomic Operation)。
定義原語的直接方法是關閉中斷,讓它的所有動作不可分割地進行完再打開中斷。
系統中的設備驅動、CPU切換、進程通信等功能中的
部分操作都可以定義為原語,使它們成為內核的組成部分。
4)系統控制的數據結構及處理
系統中用來登記狀態信息的數據結構很多,比如:
作業控制塊、進程控制塊、設備控制塊、各類鏈表、消息隊列、緩沖區、空閑區登記表、內存分配表等。
為了實現有效的管理,系統需要一些基本的操作,常見的操作有以下三種:
------ ?進程管理:進程狀態管理、進程調度和分配、創建和撤銷進程控制塊等。
------ ?存儲器管理:存儲器的空間分配和回收、內存信息保護程序、代碼對換程序等。
------ ?設備管理:緩沖區管理、設備分配和回收等。
核心態指令實際上包括系統調用類指令和一些針對時鐘、中斷和原語的操作指令。
========
操作系統的運行機制
http://www.cnblogs.com/kakawater/p/6093709.html在計算機系統中,操作系統處于中間層,向下管理和控制硬件,向上為外層軟件和用戶編寫的程序提供使用方便、功能強大的服務。操作系統的主要功能就是管理CPU、主存、I/O設備和文件,并提供支持程序并發運行的機制。
通常,操作系統提供的主要功能都是由操作系統內核程序實現的,CPU在運行上層程序時,唯一能進入內核程序運行的途徑就是中斷或異常。
中斷和異常
中斷和異常時操作系統的重要概念,中斷的引入是為了實現CPU和通道(或設備)之間的并行操作,當CPU啟動通道(或設備)進入I/O后,通道(或設備)就可以獨立工作了,CPU可以去做與此I/O不相關的事情。當通道處理完CPU交付的I/O任務之后就必須告知CPU,讓CPU繼續處理I/O操作以上的事情。在計算機中,通道(或設備)通過中斷機制來告知CPU。
由于眾多原因,當CPU在執行指令的時候可能會出現錯誤,這個錯誤就稱為”異常”。例如,CPU在執行指令時,可能會出現算術一出、零作除數、訪存越界等錯誤。此外,在操作系統中,還提供一種異常指令-陷入指令trap(即陷入指令trap是一種會產生異常的指令),用來實現系統調用(用來實現CPU切換到內核程序運行)。
中斷和異常的區別
中斷和異常都能用來讓CPU切換到內核程序運行,但是兩者的是有區別的:
調用方不一樣:中斷是由硬件來產生,而異常則是由CPU當前執行的指令的實現邏輯發生錯誤而產生。
中斷是一種通知機制,異常是一種錯誤處理機制。故中斷可以被屏蔽或者稍后處理,而異常則不能被屏蔽,并且應當立即處理。
中斷/異常的響應和處理
處理機在執行任何指令時都可能產生中斷或者異常,那么處理機如何響應中斷和異常呢?
中斷/異常響應
中斷信號是由外部設備或者時鐘部件發送給CPU的,為了能及時監測中斷信號,通過在CPU的控制部件中增加一個能夠監測中斷信號的機構來完成。該機構能夠在每條機器指令執行周期內的最后時刻掃描中斷寄存器詢問是否含有中斷信號。如果沒有中斷信號或被信號暫時被屏蔽,CPU繼續執行程序的后續指令,否則CPU停止執行當前程序的后續指令,無條件地轉入操作系統內核的中斷處理程序,我們稱該過程為中斷響應
異常是在執行指令時,由于指令的邏輯錯誤造成CPU轉入執行操作系統內核的異常處理程序。
和CPU在切換程序運行時一樣,CPU在執行異常處理程序或中斷處理程序時依舊需要涉及到對當前程序的運行現場進行保護。
斷點和恢復點
CPU一旦響應中斷,立刻開始執行內核中的中斷處理程序,當中斷處理程序結束后,重新返回中斷點執行后續指令。我們稱當中斷發生時,CPU剛執行完的那條指令的內存地址稱為”斷點”。一般情況下,斷點應為中斷的那一瞬間,CPU的PC寄存器(程序計數器)所指指令的前一條指令的內存地址。而中斷時,PC寄存器所指指令的內存地址為恢復點。
在異常發生時,返回點(即異常處理程序執行完后,CPU要執行的下一條指令的地址)會因為不同的異常而有所區別:
對于大部分由用戶程序指令執行出錯而引起的異常,操作系統的處理方式是結束所運行的程序,因此就不會回到用戶程序。
如果是通過trap指令進行系統調用,則處理完成后trap指令的下一條指令。
對于虛存系統訪存指令的缺頁異常,異常處理完后則會返回發生異常的指令重新執行該訪存指令,以保證這次訪存指令能夠順利執行。
現場保護
CPU在執行程序時,主要操作的時寄存器中的值。所謂現場信息,就是值中斷那一時刻確保存放程序繼續運行的有關信息,例如PC寄存器、通用寄存器以及一些與程序運行相關的特殊寄存器中的內存(即CPU中與程序運行相關的寄存器的狀態)。
現場信息的保護和恢復可由硬件、軟件共通完成,現場信息通常保存在操作系統中的與被中斷程序相關的數據結構中。
中斷/異常響應的CPU模式
中斷和異常的處理程序都是操作系統的內核程序,都必須在特權模式下運行,因為這些程序需要訪問外設等操作系統管理的資源或者設計系統的管理表格。
在操作系統,我們將在監督程序中CPU的兩種模式,監督模式和用戶模式,分別稱為”核心態”和”用戶態”。我們通過在CPU的狀態字寄存器中設置一個標識位,根據其當前值為1或0來分別表示處理機處在核心態或用戶態。
通常CPU執行兩種不同的程序,一種是操作系統內核程序(核心態),另一種是用戶自行編寫的程序(用戶態)或系統外層的應用程序(用戶態)。前者是后者的服務者和控制者。
中斷/異常處理程序的執行
操作系統為每個中斷/異常都創建一個處理程序,并把這些處理程序的入口地址放在主存的特定位置(主存單元),并稱這些主存單元為中斷/異常向量,或稱系統控制塊
對于不同的操作系統。中斷/異常向量中的內存細節也不完全相同。中斷/異常向量的每一個單元中出了存儲中斷/異常處理程序的入口地址外,還常用來保存CPU狀態轉換的信息。例如,中斷/異常處理程序運行需要用到的新PS寄存器值和新PC寄存器值。
在中斷/異常向量中,每一個中斷信號占用連續的兩個單元:一個用來存放中斷/異常處理程序的地址(對應PC寄存器的新值),另一個單元用來存放執行中斷/異常處理程序時CPU所處的狀態(對應PS寄存器的新值)。當響應中斷/異常時,硬件首先將當前PC和PS寄存器的值作為程序現場保存起來,然后再從中斷/異常向量中的相應單元取出要放入PC和PS寄存器中的值并放入相應的寄存器。最后再根據PC寄存器中的值去執行中斷/異常處理程序
中斷
在計算機系統中, 多個中斷可能在同一時刻產生,并且還會在前一個中斷還未處理完的情況下就產生下一個中斷。系統為了保存每個中斷信號,通常用一些中斷寄存器來將它們保存,為了區分不同的信號源,一般對中斷寄存器的各位順序編號,我們稱該編號為中斷序號,并且規定其值為1時表示有中斷信號,其值為0時表示沒有中斷信號。
在實時系統中,通過將中斷分為不同的級別,我們稱為中斷優先級,以便系統能夠立刻處理實時性要求比較高的中斷。級別高的中斷享有絕對優先處理的權利:
當級別不同的兩個以上中斷信號同時產生時,首先處理級別高的中斷。
級別高的中斷可以打斷級別低的中斷的處理過程。
當兩個級別相同的中斷產生時,根據中斷寄存器中從左至右的順序來決定處理順序。(是不是很像四則運算法則)
在實際的操作系統中,有多少中斷級別,每個中斷應該被劃分到那個級別,這些都由操作系統設計者來決定。通常來說,高速設備的中斷優先級高,低速設備的中斷優先級低。但是作為交互系統,也要考慮到用戶的特殊需求,例如用戶在輸入指令時,該輸入設備就應當設置為較高的中斷級別。
某小型機的操作系統把中斷級別分為如下三種:
時鐘中斷的中斷優先級為6
磁盤中斷的中斷優先級為5
終端等其它外設中斷的中斷優先級為4
中斷屏蔽(中斷過濾)
計算機中有那么多的屏蔽,但是有時候我們希望CPU在執行一些任務的時候可以過濾一些中斷信號,讓CPU專注于當前的任務,在一點在很多場景下都很有用,譬如當多個程序同時操縱一塊內存的時候很有用。
所謂的”中斷屏蔽”通常是指禁止響應中斷。在可編程中斷控制器中,CPU可以執行特權指令來設置可編程中斷控制器的屏蔽碼,這樣即使硬件發現具有屏蔽碼的中斷產生,也不會通知處理機,但還是會保存此次中斷以便將來屏蔽解除時由CPU來處理. 我們稱這種方式為軟屏蔽
處理器優先級是指當前CPU正在運行程序的中斷響應級別。當處理器處于某一處理器優先級時,就會只允許處理響應級別比該級別高的中斷,而屏蔽低于或等于該優先級的中斷。
系統調用(trap指令)
當用戶態下的程序通過trap指令進行系統調用的時候,需要進行調用參數,以便根據系統調用表調用相應的系統調用處理程序。
為了方便高級語言程序使用系統調用,通常提供一個系統調用庫,其中包含許多系統調用借口函數,這些函數看上去就是一些普通的子程序,但是實則這些函數往往是有為數不多的幾條匯編指令實現的,而且這些匯編指令中必須包含一條trap指令,這樣才能保證指令trap指令時處理器控制轉移至操作系統內核的相應程序。
使用系統提供的系統調用庫函數進行系統調用不但可以使用戶不必關心系統調用的細節,而且可以避免由用戶直接使用trap指令可能引起的錯誤。
ABI(Application Binary Interface,應用程序二進制接口)
在進行系系統調用時,系統調用庫函數需要向操作系統傳遞什么樣的調用型號?該系統調用需要什么參數?以及如何傳遞參數(顯然是通過寄存器來傳遞)?如何接收系統調用的返回值(顯然也是通過寄存器)等這一系列操作都會在操作系統的ABI文檔中說明,例如上層用戶應該使用哪些寄存器來傳遞參數。
事實上,我們可以自己基于操作系統的ABI文檔來實現一個系統調用庫,這在沒有與我們使用的高級語言相對應的系統調用庫時極為有用。
========
操作系統的運行機制
http://c.biancheng.net/cpp/html/2583.html計算機系統中,通常CPU執行兩種不同性質的程序:一種是操作系統內核程序;另一種是用戶自編程序或系統外層的應用程序。對操作系統而言,這兩種程序的作用不同,前者是后者的管理者,因此“管理程序”要執行一些特權指令,而“被管理程序”出于安全考慮不能執行這些指令。所謂特權指令,是指計算機中不允許用戶直接使用的指令,如I/O指令、 置中斷指令,存取用于內存保護的寄存器、送程序狀態字到程序狀態字寄存器等指令。操作系統在具體實現上劃分了用戶態(目態)和核心態(管態),以嚴格區分兩類程序。
在軟件工程思想和結構程序設計方法的影響下誕生的現代操作系統,幾乎都是層次式的結構。操作系統的各項功能分別被設置在不同的層次上。一些與硬件關聯較緊密的模塊,諸如時鐘管理、中斷處理、設備驅動等處于最底層。其次是運行頻率較髙的程序,諸如進程管理、存儲器管理和設備管理等。這兩部分內容構成了操作系統的內核。這部分內容的指令操作工作在核心態。
內核是計算機上配置的底層軟件,是計算機功能的延伸。不同系統對內核的定義稍有區別,大多數操作系統內核包括四個方面的內容。
1) 時鐘管理
在計算機的各種部件中,時鐘是最關鍵的設備。時鐘的第一功能是計時,操作系統需要通過時鐘管理,向用戶提供標準的系統時間。另外,通過時鐘中斷的管理,可以實現進程的切換。諸如,在分時操作系統中,釆用時間片輪轉調度的實現;在實時系統中,按截止時間控制運行的實現;在批處理系統中,通過時鐘管理來衡量一個作業的運行程度等。因此,系統管理的方方面面無不依賴于時鐘。
2) 中斷機制
引入中斷技術的初衷是提高多道程序運行環境中CPU的利用率,而且主要是針對外部設備的。后來逐步得到發展,形成了多種類型,成為操作系統各項操作的基礎。例如,鍵盤或鼠標信息的輸入、進程的管理和調度、系統功能的調用、設備驅動、文件訪問等,無不依賴于中斷機制。可以說,現代操作系統是靠中斷驅動的軟件。
中斷機制中,只有一小部分功能屬于內核,負責保護和恢復中斷現場的信息,轉移控制權到相關的處理程序。這樣可以減少中斷的處理時間,提高系統的并行處理能力。
3) 原語
按層次結構設計的操作系統,底層必然是一些可被調用的公用小程序,它們各自完成一個規定的操作。其特點是:
它們處于操作系統的最底層,是最接近硬件的部分。
這些程序的運行具有原子性——其操作只能一氣呵成(這主要是從系統的安全性和便于管理考慮的)。
這些程序的運行時間都較短,而且調用頻繁。
通常把具有這些特點的程序稱為原語(Atomic Operation)。定義原語的直接方法是關閉中斷,讓它的所有動作不可分割地進行完再打開中斷。
系統中的設備驅動、CPU切換、進程通信等功能中的部分操作都可以定義為原語,使它們成為內核的組成部分。
4) 系統控制的數據結構及處理
系統中用來登記狀態信息的數據結構很多,比如作業控制塊、進程控制塊(PCB)、設備控制塊、各類鏈表、消息隊列、緩沖區、空閑區登記表、內存分配表等。為了實現有效的管理,系統需要一些基本的操作,常見的操作有以下三種:
進程管理:進程狀態管理、進程調度和分派、創建與撤銷進程控制塊等。
存儲器管理:存儲器的空間分配和回收、內存信息保護程序、代碼對換程序等。
設備管理:緩沖區管理、設備分配和回收等。
從上述內容可以了解,核心態指令實際上包括系統調用類指令和一些針對時鐘、中斷和原語的操作指令。
========
操作系統運行環境和運行機制
http://blog.csdn.net/wyi06/article/details/548081451)操作系統運行環境(物理機器界面):
a.CPU狀態(模式)
處理器由運算器,控制器,一系列的寄存器(用戶可見寄存器,控制和狀態寄存器)以及高速緩存構成
常見的控制和狀態寄存器:
程序計數器(PC:Program Counter),記錄將要取出的指令地址
指令寄存器(IR:Instruction Register),記錄最近取出的指令
程序狀態字(PSW:Program Status Word),記錄處理的運行狀態如條件碼、模式、控制位等信息
操作系統的需求——保護
需要硬件提供基本運行機制:
處理器具有特權級別,能在不同的特權級運行不同的指令集合
硬件機制可將OS與用戶程序隔離
處理器的狀態(模式MODE)
在PSW中專門設置一位,根據運行程序對資源和指令的使用權限而設置不同的CPU狀態
操作系統的兩種狀態
內核態:運行操作系統
用戶態:運行用戶程序
特權指令:只能由操作系統使用(啟動I/O,內存清零,修改程序狀態字,設置時鐘,允許/禁止中斷,停機)
非特權指令:用戶程序可以使用的命令(控制轉移,算術運算,
訪管指令(又稱陷入指令,提供給用戶程序的接口,用于調用操作系統的功能(服務)),取數指令)
CPU狀態之間的轉換
用戶態-->內核態(唯一途徑:中斷/異常/陷入機制)
內核態-->用戶態 設置PSW
b.中斷/異常機制
中斷/異常的概念:CPU對系統發生的某個事件做出的一種反應
事件的發生改變了處理器控制流
中斷(外中斷如I/O中斷,時鐘中斷,硬件故障),如為了支持CPU和設備之間的并行操作;
異常(內中斷如系統調用,頁故障,保護性異常,斷點指令,其他程序異常)表示CPU執行指令本身出現的問題。
特點:隨機發生的,自動處理的,可恢復的
中斷/異常機制工作原理
硬件:(發現中斷、接受中斷)捕獲中斷源發出的中斷/異常請求,以一定的方式響應,將處理器控制權交給特定的處理程序
軟件:(中斷異常處理程序)識別中斷/異常類型并完成相應的處理
中斷響應過程:
設備發中斷信號,硬件保存現場,根據中斷碼查表,把中斷處理程序入口地址等推送到相應的寄存器,執行中斷程序
中斷處理程序:
設計操作系統時,為每一類中斷/異常事件編好相應的處理程序,并設置好中斷向量表
系統運行時若響應中斷,中斷硬件部件將CPU控制權轉給中斷處理程序:
保存相關寄存器信息,分析中斷/異常的具體原因,執行對應的處理功能,恢復現場,返回被事件大段的程序
2)操作系統運行機制(虛擬機界面):
a.系統調用(操作系統功能調用)
定義:用戶在編程時可以調用的操作系統功能
作用:是操作系統提供給編程人員的唯一接口;使CPU狀態從用戶態陷入內核態
系統調用機制的設計:
中斷/異常機制:支持系統調用服務的實現
選擇一條特殊指令:陷入指令(訪管指令):引發異常,完成用戶態到內核態的切換
系統調用號和參數:每個系統調用都事先給定一個編號(功能號)
系統調用表:存放系統調用服務例程的入口地址
用戶程序的參數傳遞給內核:
方法1:由陷入指令自帶參數:陷入指令的長度有限,且還要攜帶系統調用功能號,只能自帶有限參數
方法2:通過通用寄存器傳遞參數:這些寄存器是操作系統和用戶程序都能訪問的,但寄存器的個數會限制傳遞參數的數量
方法3:在內存中開辟專用堆棧區來傳遞參數
系統調用的執行過程
當CPU執行到特殊的陷入指令時:
中斷/異常機制:硬件保護現場;通過中斷向量表把控制權轉給系統調用總入口程序
系統調用總入口程序:保存現場;將參數保存在堆棧里;通過查系統調用表把控制權轉給相應的系統調用處理例程或內核函數
執行系統調用例程
恢復現場,返回用戶程序
操作系統概述進程/線程模型
========
操作系統-鎖機制
http://blog.csdn.net/lz20120808/article/details/51707247計算機操作系統鎖機制.
在多線程編程中,操作系統引入了鎖機制。通過鎖機制,能夠保證在多核多線程環境中,在某一個時間點上,只能有一個線程進入臨界區代碼,從而保證臨界區中操作數據的一致性。
所謂的鎖,可以理解為內存中的一個整型數,擁有兩種狀態:空閑狀態和上鎖狀態。加鎖時,判斷鎖是否空閑,如果空閑,修改為上鎖狀態,返回成功;如果已經上鎖,則返回失敗。解鎖時,則把鎖狀態修改為空閑狀態。?
加鎖過程用如下偽碼表示:?
1、read lock;?
2、判斷lock狀態;?
3、如果已經加鎖,失敗返回;?
4、把鎖狀態設置為上鎖;?
5、返回成功。?
雖然每一步是原子性的,但是每一步之間卻是可以中斷的。比如進程A在執行完2后發生中斷,中斷中進程B也執行了加鎖過程,返回中斷后就會發生兩個進程都會加鎖。?
對于這個問題,計算機已經解決,方法是采用原子級匯編指令test and set 和swap。
死鎖的概念.
死鎖: 是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去.此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程.?
比如 兩只羊過獨木橋。進程比作羊,資源比作橋。若兩只羊互不相讓,爭著過橋,就產生死鎖。
死鎖的原因.
主要原因(1) 因為系統資源不足。(2) 進程運行推進的順序不合適,保證有先后順序。(3) 資源分配不當等。
死鎖的必要條件.
產生死鎖的四個必要條件:?
(1) 互斥條件:一個資源每次只能被一個進程使用。?
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。?
(3) 不剝奪條件: 進程已獲得的資源,在末使用完之前,不能強行剝奪。?
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。存在一個進程等待序列{P1,P2,…,Pn},其中P1等待P2所占有的某一資源,P2等待P3所占有的某一 源,……,而Pn等待P1所占有的的某一資源,形成一個進程循環等待環。?
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
解決死鎖的四個方式.?
1)忽略該問題。例如鴕鳥算法,該算法可以應用在極少發生死鎖的的情況下。為什么叫鴕鳥算法呢,(鴕鳥策略)?
2)檢測死鎖并且恢復。(檢測與解除策略)?
3)仔細地對資源進行動態分配,以避免死鎖。(避免策略)?
4)通過破除死鎖四個必要條件之一,來防止死鎖產生。(預防策略)
C++多線程開發中,容易出現死鎖導致程序掛起的現象。?
解決步驟分為三步:?
1、檢測死鎖線程。?
2、打印線程信息。?
3、修改死鎖程序。
進程(Process)和線程(Thread).
進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。擁有獨立的內存單元。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。但是不能獨立運行,必須依存在應用程序中,由應用程序提供多個線程執行控制。線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以并發執行。?
進程與應用程序的區別在于應用程序作為一個靜態文件存儲在計算機系統的硬盤等存儲空間中,而進程則是處于動態條件下由操作系統維護的系統資源管理實體。
進程的狀態轉換圖,及導致轉換的事件.?
三個狀態:?
1)就緒狀態 進程已獲得除處理機外的所需資源,等待分配處理機資源,只要分配到CPU就可執行。在某一時刻,可能有若干個進程處于該狀態。 ?
2)運行狀態 占用處理機資源運行,處于此狀態的進程的數目小于等于CPU的數目。 ?
3)阻塞狀態 由于進程等待某種條件(如I/O操作或進程同步),在條件滿足之前無法繼續執行。該事件發生前即使把處理機分配給該進程,也無法運行。
========
鎖機制與原子操作
http://www.cnblogs.com/kissdodog/archive/2013/04/07/3003822.html一、線程同步中的一些概念
1.1臨界區(共享區)的概念
在多線程的環境中,可能需要共同使用一些公共資源,這些資源可能是變量,方法邏輯段等等,這些被多個線程共用的區域統稱為臨界區(共享區),臨界區的資源不是很安全,因為線程的狀態是不定的,所以可能帶來的結果是臨界區的資源遭到其他線程的破壞,我們必須采取策略或者措施讓共享區數據在多線程的環境下保持完成性不讓其受到多線程訪問的破壞。
1.2基元用戶模式
基元用戶模式是指使用cpu的特殊指令來調度線程,所以這種協調調度線程是在硬件中進行的所以得出了它第一些優點:
速度特別快;
線程阻塞時間特別短;
但是由于該模式中的線程可能被系統搶占,導致該模式中的線程為了獲取某個資源,而浪費許多cpu時間,同時如果一直處于等待的話會導致”活鎖”,也就是既浪費了內存,又浪費了cpu時間,這比下文中的死鎖更可怕,那么如何利用強大的cpu時間做更多的事呢?那就引出了下面的一個模式
? 1.3基元內核模式
該模式和用戶模式不同,它是windows系統自身提供的,使用了操作系統中內核函數,所以它能夠阻塞線程提高了cpu的利用率,同時也帶來了一個很可怕的bug,死鎖,可能線程會一直阻塞導致程序的奔潰,常用的內核模式的技術例如Monitor,Mutex,等等會在下一章節介紹。本章將詳細討論鎖的概念,使用方法和注意事項
? 1.4原子性操作
如果一個語句執行一個單獨不可分割的指令,那么它是原子的。嚴格的原子操作排除了任何搶占的可能性,更方便的理解是這個值永遠是最新的,在c#中原子操作如下圖所示:其實要符合原子操作必須滿足以下條件c#中如果是32位cpu的話,為一個少于等于32位字段賦值是原子操作,其他(自增,讀,寫操作)的則不是。對于64位cpu而言,操作32或64位的字段賦值都屬于原子操作其他讀寫操作都不能屬于原子操作相信大家能夠理解原子的特點,所以在使用原子操作時也需要注意當前操作系統是32位或是64位cpu或者兩者皆要考慮。
1.5非阻止同步
非阻止同步:不阻止其他線程的情況下實現同步。就是利用原子性操作實現線程間的同步,不刻意阻塞線程,減少相應線程的開銷,interlocked類便是c#中非阻止同步的理念所產生的線程同步技術。
1.6阻止同步
阻止同步:阻止其他線程,同一時間只允許單個線程訪問臨界資源。其實阻止同步也是基元內核模式的特點之一。
例如c# 中的鎖機制,及mutex,monitor等都屬于阻止同步,他們的根本目的是,以互斥的效果讓同一時間只有一個線程能夠訪問共享區,其他線程必須阻止等待,直到該線程離開共享區后,才讓其他一個線程訪問共享區,阻止同步缺點也是容易產生死鎖,但是阻止同步提高了cpu時間的利用率。
二、為何需要同步
當多個線程同時訪問某個資源,可能造成意想不到的結果。如多個線程同時訪問靜態資源。
復制代碼
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? //初始化10個線程1去訪問num
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static int num = 0;
? ? ? ? static void Run(object state)
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("當前數字:{0}", ++num);
? ? ? ? }
? ? }
復制代碼
輸出如下:
我們看到,num++按照邏輯,應該是1,2,3,4,5,6,7,8,9,10。這就是多個線程去訪問,順序亂套了。這時候就需要同步了。
三、原子操作同步原理
Thread類中的VolatileRead和VolatileWrite方法:
VolatileWrite:當線程在共享區(臨界區)傳遞信息時,通過此方法來原子性的寫入最后一個值;
VolatileRead:當線程在共享區(臨界區)傳遞信息時,通過此方法來原子性的讀取第一個值;
復制代碼
? ? class Program
? ? {
? ? ? ? static Int32 count;//計數值,用于線程同步 (注意原子性,所以本例中使用int32)
? ? ? ? static Int32 value;//實際運算值,用于顯示計算結果
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? //讀線程
? ? ? ? ? ? Thread thread2 = new Thread(new ThreadStart(Read));
? ? ? ? ? ? thread2.Start();
? ? ? ? ? ? //寫線程
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.Sleep(20);
? ? ? ? ? ? ? ? Thread thread = new Thread(new ThreadStart(Write));
? ? ? ? ? ? ? ? thread.Start();
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// 實際運算寫操作
? ? ? ? /// </summary>
? ? ? ? private static void Write()
? ? ? ? {
? ? ? ? ? ? Int32 temp = 0;
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? temp += 1;
? ? ? ? ? ? }
? ? ? ? ? ? //真正寫入
? ? ? ? ? ? value += temp;
? ? ? ? ? ? Thread.VolatileWrite(ref count, 1);
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// ?死循環監控讀信息
? ? ? ? /// </summary>
? ? ? ? private static void Read()
? ? ? ? {
? ? ? ? ? ? while (true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //死循環監聽寫操作線執行完畢后立刻顯示操作結果
? ? ? ? ? ? ? ? if (Thread.VolatileRead(ref count) > 0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("累計計數:{1}", Thread.CurrentThread.ManagedThreadId, value);
? ? ? ? ? ? ? ? ? ? count = 0;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
復制代碼
輸出如下:
三、Volatile關鍵字
Volatile關鍵字的本質含義是告訴編譯器,聲明為Volatile關鍵字的變量或字段都是提供給多個線程使用的。Volatile無法聲明為局部變量。作為原子性的操作,Volatile關鍵字具有原子特性,所以線程間無法對其占有,它的值永遠是最新的。
Volatile支持的類型:
引用類型;
指針類型(在不安全的上下文中);
類型,如 sbyte、byte、short、ushort、int、uint、char、float 和 bool;
具有以下基類型之一的枚舉類型:byte、sbyte、short、ushort、int 或 uint;
已知為引用類型的泛型類型參數;
IntPtr 和 UIntPtr;
復制代碼
class Program
? ? {
? ? ? ? static volatile Int32 count;//計數值,用于線程同步 (注意原子性,所以本例中使用int32)
? ? ? ? static Int32 value;//實際運算值,用于顯示計算結果
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? //開辟一個線程專門負責讀value的值,這樣就能看見一個計算的過程
? ? ? ? ? ? Thread thread2 = new Thread(new ThreadStart(Read));
? ? ? ? ? ? thread2.Start();
? ? ? ? ? ? //開辟10個線程來負責計算,每個線程負責1000萬條數據
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.Sleep(20);
? ? ? ? ? ? ? ? Thread thread = new Thread(new ThreadStart(Write));
? ? ? ? ? ? ? ? thread.Start();
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// 實際運算寫操作
? ? ? ? /// </summary>
? ? ? ? private static void Write()
? ? ? ? {
? ? ? ? ? ? Int32 temp = 0;
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? temp += 1;
? ? ? ? ? ? }
? ? ? ? ? ? value += temp;
? ? ? ? ? ? //告訴監聽程序,我改變了,讀取最新吧!
? ? ? ? ? ? count = 1;
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// ?死循環監聽
? ? ? ? /// </summary>
? ? ? ? private static void Read()
? ? ? ? {
? ? ? ? ? ? while (true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (count == 1)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("累計計數:{1}", Thread.CurrentThread.ManagedThreadId, value);
? ? ? ? ? ? ? ? ? ? count = 0;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
復制代碼
輸出:
四、lock關鍵字
lock的作用在于同一時間確保一個對象只允許一個線程訪問。
lock的語法如下:
? ?static object obj = new object();
? ?lock (obj)
? ?{
? ? //語句塊
? ?}
我們使用lock來改寫上面的示例:
復制代碼
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? //初始化10個線程1去訪問num
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static int num = 0;
? ? ? ? static object obj = newobject();
? ? ? ? static void Run(object state)
? ? ? ? {
? ? ? ? ? ? lock (obj)
? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("當前數字:{0}", ++num);
? ? ? ? ? ? }
? ? ? ? }
? ? }
復制代碼
輸出如下:
五、Monitor.Enter與Monitor.Exit
Monitor.Enter和Monitor.Exit這個東西跟lock的作用一樣。事實上。lock就是Monitor.Enter和Monitor.Exit的包裝。
下面用Monitor.Enter與Monitor.Exit來實現相同的代碼:
復制代碼
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? //初始化10個線程1去訪問num
? ? ? ? ? ? for (int i = 0; i < 10; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static int num = 0;
? ? ? ? static object obj = new object();
? ? ? ? static void Run(object state)
? ? ? ? {
? ? ? ? ? ? //獲取排他鎖
? ? ? ? Monitor.Enter(obj);
? ? ? ? ? ? Console.WriteLine("當前數字:{0}", ++num);
? ? ? ? ? ? //釋放排它鎖
? ? ? ? Monitor.Exit(obj);
? ? ? ? }
? ? }
復制代碼
六、Monitor.Wait與Monitor.Pulse
Wait() 和 Pulse() 機制用于線程間交互:
Wait() 釋放鎖定資源,進入等待狀態直到被喚醒;
Pulse() 和 PulseAll() 方法用來通知Wait()的線程醒來;
復制代碼
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? Thread t1 = new Thread(Run1);
? ? ? ? ? ? Thread t2 = new Thread(Run2);
? ? ? ? ? ? t1.Start();
? ? ? ? ? ? t1.Name = "劉備";
? ? ? ? ? ? t2.Start();
? ? ? ? ? ? t2.Name = "關羽";
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static object obj = new object();
? ? ? ? static void Run1(object state)
? ? ? ? {
? ? ? ? ? ? Monitor.Enter(obj);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + ":二弟,你上哪去了?");
? ? ? ? ? ? Monitor.Wait(obj); ? ? ?//暫時釋放鎖,讓關羽線程進入
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + ":你混蛋!");
? ? ? ? ? ? ? ??
? ? ? ? ? ? Monitor.Pulse(obj); ? ? //喚醒關羽線程?
? ? ? ? ? ? Monitor.Exit(obj);
? ? ? ? }
? ? ? ? static void Run2(object state)
? ? ? ? {
? ? ? ? ? ? Monitor.Enter(obj);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + ":老子跟曹操了!");
? ? ? ? ? ? Monitor.Pulse(obj); ? ? //喚醒劉備線程
? ? ? ? ? ? Monitor.Wait(obj); ? ? //暫停本線程
? ? ? ? ? ??
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + ":投降吧,曹孟德當世英雄,豎子不足與謀!!");
? ? ? ? ? ? Monitor.Exit(obj);
? ? ? ? }
? ? }
復制代碼
輸出如下:
七、讀寫鎖ReadWriterLock
寫入串行,讀取并行;
如果程序中大部分都是讀取數據的,那么由于讀并不影響數據,ReadWriterLock類能夠實現”寫入串行“,”讀取并行“。
常用方法如下:
AcquireWriterLock: 獲取寫入鎖; ReleaseWriterLock:釋放寫入鎖。
AcquireReaderLock: 獲取讀鎖; ReleaseReaderLock:釋放讀鎖。
UpgradeToWriterLock:將讀鎖轉為寫鎖;DowngradeFromWriterLock:將寫鎖還原為讀鎖。
復制代碼
? ?class Program
? ? {
? ? ? ? static List<string> ListStr = new List<string>();
? ? ? ? static ReaderWriterLock rw = new System.Threading.ReaderWriterLock();
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? Thread t1 = new Thread(Run1);
? ? ? ? ? ? Thread t2 = new Thread(Run2);
? ? ? ? ? ? t1.Start();
? ? ? ? ? ? t1.Name = "劉備";
? ? ? ? ? ? t2.Start();
? ? ? ? ? ? t2.Name = "關羽";
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static object obj = new object();
? ? ? ? static void Run1(object state)
? ? ? ? {
? ? ? ? ? ? //獲取寫鎖2秒
? ? ? ? ? ? rw.AcquireWriterLock(2000);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "正在寫入!");
? ? ? ? ? ? ListStr.Add("曹操混蛋");
? ? ? ? ? ? ListStr.Add("孫權王八蛋");
? ? ? ? ? ? Thread.Sleep(1200);
? ? ? ? ? ? ListStr.Add("周瑜個臭小子");
? ? ? ? ? ? rw.ReleaseWriterLock();
? ? ? ? ? ??
? ? ? ? }
? ? ? ? //此方法異常,超時,因為寫入時不允許讀(那么不用測也能猜到更加不允許寫咯)
? ? ? ? static void Run2(object state)
? ? ? ? {
? ? ? ? ? ? //獲取讀鎖1秒
? ? ? ? ? ? rw.AcquireReaderLock(1000);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "正在讀取!");
? ? ? ? ? ? foreach (string str in ListStr)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine(str);
? ? ? ? ? ? }
? ? ? ? ? ? rw.ReleaseReaderLock();
? ? ? ? }
? ? }
復制代碼
異常如下:
下面是讀取并行的例子:
復制代碼
? ? class Program
? ? {
? ? ? ? static List<string> ListStr = new List<string>();
? ? ? ? static ReaderWriterLock rw = new System.Threading.ReaderWriterLock();
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? ListStr.Add("貂蟬");
? ? ? ? ? ? ListStr.Add("西施");
? ? ? ? ? ? ListStr.Add("王昭君");
? ? ? ? ? ? Thread t1 = new Thread(Run1);
? ? ? ? ? ? Thread t2 = new Thread(Run2);
? ? ? ? ? ? t1.Start();
? ? ? ? ? ? t1.Name = "劉備";
? ? ? ? ? ? t2.Start();
? ? ? ? ? ? t2.Name = "關羽";
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? ? ? static object obj = new object();
? ? ? ? static void Run1(object state)
? ? ? ? {
? ? ? ? ? ? //獲取寫鎖2秒
? ? ? ? ? ? rw.AcquireReaderLock(2000);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "正在讀取!");
? ? ? ? ? ? foreach (string str in ListStr)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "在讀:" + str);
? ? ? ? ? ? }
? ? ? ? ? ? rw.ReleaseReaderLock();
? ? ? ? ? ??
? ? ? ? }
? ? ? ? //此方法異常,超時,因為寫入時不允許讀(那么不用測也能猜到更加不允許寫咯)
? ? ? ? static void Run2(object state)
? ? ? ? {
? ? ? ? ? ? //獲取讀鎖1秒
? ? ? ? ? ? rw.AcquireReaderLock(1000);
? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "正在讀取!");
? ? ? ? ? ? foreach (string str in ListStr)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine(Thread.CurrentThread.Name + "在讀:" + str);
? ? ? ? ? ? }
? ? ? ? ? ? rw.ReleaseReaderLock();
? ? ? ? }
? ? }
復制代碼
輸出如下:
總結:寫入鎖與任何鎖都不兼容,讀取鎖與讀取鎖可以兼容。
======== 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的操作系统机制学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 操作http协议学习总结
- 下一篇: 操作系统pv操作学习总结