學習I/O核心子系統相關的一系列功能。
設備獨立性軟件、設備驅動程序、中斷處理程序這三層其實是屬於操作系統的內核部分的,所以它們也稱作“I/O核心子系統”,又可以簡稱為“I/O系統”。在考研當中我們需要重點掌握的是:I/O調度、設備保護、假脫機技術(SPOOLing技術)、設備分配與回收、緩沖區管理(即緩沖與高速緩存)這幾種功能的原理和實現。
那我們來看一下這些事情分別需要在哪些層次實現。所有和硬件直接相關的,那肯定是設備驅動程序和中斷處理程序需要負責的。但是咱們剛才提到的那些功能當中都沒有直接與硬件相關的。
這個地方大家可能會比較奇怪,剛才不是說I/O核心子系統(I/O系統)要實現的是這些技術嗎?而假脫機技術(SPOOLing技術)看起來並不是I/O核心子系統(I/O系統)來實現的。一般來說假脫機技術(SPOOLing技術)都需要使用到磁盤這種設備的設備獨立性軟件這一層的服務,所以假脫機技術(SPOOLing技術)一般來說都是在用戶層軟件實現的。
但是在咱們408的考研大綱中,又把假脫機技術(SPOOLing技術)把它歸為I/O核心子系統(I/O系統)要實現的一個功能。所以在考試的時候如果我們遇到了,那還是以這個大綱為准。不過大家也需要知道,在實際應用中,其實假脫機技術是用戶層軟件這兒實現的。
那這個小節中我們先簡單地介紹兩個我們很熟悉的功能的實現,一個是I/O調度,一個是設備保護。首先來看I/O調度,其實I/O調度和處理機調度是很類似的,就是用某一種算法來確定一個好的順序,來處理各個I/O請求。就比如說咱們在上一章學過的磁盤調度,其實就是用某一種算法,來確定應該先滿足哪些磁盤的訪問請求。那由於磁盤其實也是一種I/O設備,所以其實磁盤調度也是I/O調度的問題。
那除了磁盤之外,像什么打印機等等這些設備,也同樣是可以采用類似於先來先服務啊,優先級算法或者短作業優先等等這些算法來確定一個合適的I/O調度順序。那這些咱們之前已經訓練過很多次了,所以這兒就不再贅述。大家只需要根據題目中提供的算法的名字,再結合咱們之前學習過的那些調度算法的思想來分析、做題就可以了,所以I/O調度其實是我們很熟悉的一個內容。
那接下來我們再來看一下設備保護。這一點其實咱們在上小節也簡單地提及過,其實怎么實現設備保護這一點咱們也已經很熟悉了。因為在UNIX系統當中,設備會被看作是一種特殊的文件,因此其實系統也會為各個設備建立一個相應的FCB,也就是文件控制塊。那就像咱們之前在文件管理的章節中學到的一樣,不同的用戶對於不同的文件是有不同的訪問權限的。但它其實上其實也是文件保護那一塊所需要做的事情。那這個知識點如果回憶不起來的話,可以再參考咱們在第四章中文件保護那個小節當中講到的內容。
好的,那么這個小節我們只是簡單地對I/O核心子系統(I/O系統)需要實現哪些功能進行了一個簡要的概述,並且簡單提了一下I/O調度和設備保護應該怎么實現,這都是咱們很熟悉的內容。那之后的小節中我們還會展開介紹設備分配與回收、內存緩沖區管理(緩沖與高速緩存)應該怎么實現。而假脫機技術(或者叫SPOOLing技術)它在實際當中本來是在用戶層軟件當中實現的,但是在408考研大綱中是在設備獨立性軟件中實現,也把它歸為I/O核心子系統(I/O系統)要實現的功能之一,所以我們按照從上至下的順序在下個小節會先介紹假脫機技術(或者叫SPOOLing)的實現。
學習假脫機技術(又叫SPOOLing技術)。
假脫機技術(又叫SPOOLing技術)其實是用軟件的方式來實現了脫機技術。那最后我們會以共享打印機的案例來分析一下就是假脫機技術的具體實現和應用。
咱們在第一章操作系統的發展歷程那個小節當中學過,在剛開始就是手工輸入的這個沒有操作系統的階段,都是需要用這樣的紙帶機,很慢速的紙帶機,來把這些程序員的這些程序讀入,然后CPU需要直接和這個紙帶機交互。所以由於紙帶機的速度特別慢,因此這個輸入還有把結果輸出的過程都會很慢。所以雖然CPU的處理速度很快,但是在數據輸入和數據輸出的時候,CPU依然需要遷就紙帶機的這個慢速的輸入輸出。
所以在這個階段就暴露出了很嚴重的這種速度矛盾,這對CPU這種昂貴的資源是一種極大的浪費。
所以在之后的批處理階段人們引入了脫機技術,那剛開始的脫機技術是用磁帶來實現的。引入了脫機技術之后,程序員可以先用紙帶機把自己的這些程序輸進數據輸入到磁帶當中,而磁帶的速度要比紙帶機要快的很多。而這個輸入的過程是由一台專門的外圍控制機來實現的。那由於這些程序的數據首先被輸入到了這個更快速的磁帶當中,而之后CPU可以直接從這個磁帶中讀取想要的這些輸入數據。因此這就很大地緩解了這種速度的矛盾。也就是說有了脫機輸入技術之后,在數據輸入的時候速度就快了很多。那在輸出的時候其實也一樣。主機首先會把數據輸出到一個很快速的磁帶上,之后又會由一個外圍控制機控制要把這磁帶當中的數據依次地輸出到這些這個慢速的磁帶機上。那同樣的,由於磁帶的速度比紙帶機的速度快很多,因此CPU在輸出的時候就可以節省很多等待輸出完成的這個時間了。那這樣的話就大大地提升了CPU的利用率。其實這個輸入和輸出它是由一個外圍控制機來實現的,那顯然這個輸入和輸出的過程其實並不需要主機或者說CPU的干預和控制,這樣的話就可以讓CPU有更多的時間去處理別的那些計算任務了。所以其實引入了脫機技術之后,除了緩解了I/O設備還有快速的CPU之間的速度矛盾之外,還有一個好處就是,即使CPU正在忙碌,但是也可以在外圍控制機的控制下,先把數據預先地把它輸入到這個磁帶當中。而在輸出的時候,即使這個紙帶機現在正在忙碌,但是CPU也可以先把數據輸出到這個磁帶當中。
那基於脫機思想,人們又發明了所謂的假脫機技術,又叫“SPOOLing技術”。假脫機技術當中的脫機,其實是由軟件的方式來模擬實現的。那一個SPOOLing系統或者叫假脫機系統,一般來說由這樣的一些部分組成。
那這是輸入井和輸出井的一個作用。那除了這個磁帶之外,外圍控制機其實也是實現脫機技術當中一個很重要的一個部件。
那外圍控制機就是由一個輸入進程和一個輸出進程來實現的。輸入進程就是模擬脫機輸入時候的外圍控制機,而輸出進程是模擬脫機輸出時候的外圍控制機。那顯然這個輸入進程和輸出進程肯定需要和用戶進程並發地執行才可以完成這種模擬脫機輸入和模擬脫機輸出的過程。因此SPOOLing技術肯定是需要有多道程序技術的支持的。
輸入進程就是模擬這個外圍控制機的功能。也就是說,當有數據需要從設備輸入到計算機的時候,這個輸入進程其實是由軟件的方式模擬外圍控制機。這個輸入進程會把這些要輸入的數據放到磁盤的輸入井當中,而輸出進程在模擬脫機輸出的時候原理也類似,無非就是從磁盤的輸出井當中取出數據,然后輸出到這個設備上。
那這個地方大家還需要注意,在內存當中也會開辟這樣的兩個緩沖區,一個叫輸入緩沖區,一個輸出緩沖區。其實它們的作用就是在模擬脫機輸入還有模擬脫機輸出的時候作為一個數據的中轉站這樣的一個角色。所以輸入進程在實現模擬脫機輸入的時候,其實是先接收了這個輸入設備的數據,然后把這些數據先放到輸入緩沖區里,之后再把輸入緩沖區當中的數據放到磁盤的輸入井當中。而數據輸出的時候,其實是由輸出進程從輸出井當中取出數據然后放到這個內存的輸出緩沖區當中。之后再由輸出緩沖區往輸出設備寫出數據。那這就是假脫機技術的一個原理。大家再結合剛才的圖再自己理解一下。
那接下來我們來看一個具體的假脫機技術(SPOOLing技術)的應用——共享打印機的實現。打印機它肯定是一種“獨占式的設備”,因為打印機的這個速度很慢,這個這一點大家在生活當中也有所體會。所以打印機這種設備在一段時間內肯定是只能為一個用戶、一個進程服務的。如果打印機設備同時處理多個進程的請求的話,那么就有可能會導致各個進程的打印輸出結果相互串行,就是混雜在一起的這種情況,那這顯然不是我們期待的。
只有第一個進程使用完了打印機並且釋放了這個資源之后,第二個進程才可以被激活然后繼續發出這個打印請求。那既然打印機它是一種獨占式的設備,為什么我們這兒還把它稱作共享打印機呢?
那其實這兒所謂的共享就是用SPOOLing技術,也就是假脫機技術來實現的。而是由一個叫做假脫機管理進程的東西為每個用戶進程做這樣的兩件事。
也就是說其實這個緩沖區是在磁盤里的緩沖區,而不是內存中的緩沖區。
那之后假脫機管理進程會把用戶進程想要打印輸出的數據
把它先放到給它申請的這個緩沖區當中。這是這個管理進程做的第一件事。
第二件事它還會為這個用戶進程申請一張空白的打印請求表,並且把用戶的打印請求各種參數填入表中。所以其實這個表就是用來說明用戶的打印數據放在哪個緩沖區里,存放在什么地方等等這一系列信息的。所以其實不要被這個浮華的名詞所迷惑,它的本質無非就是一個打印任務的說明書。那接下來這個打印任務的說明書會被掛到一個叫做脫機文件隊列的隊列上。
那這個文件隊列其實就是一系列的打印任務的隊列。所以之后這個輸出進程會從這個任務隊列當中取出某一個任務,然后根據這個打印請求表的說明來執行相應的那些打印任務。
那比如說剛才這個用戶進程提出的打印任務此時就是掛在這個隊列的隊頭的。所以此時輸出進程應該先滿足的是隊頭的這個任務。因此,根據這個打印任務表它就可以知道此時要打印輸出的這些數據是在這一塊緩沖區當中的。
所以它會從這塊緩沖區當中讀出這個數據,然后把這個數據首先讀入到內存的輸出緩沖區當中。
之后再從輸出緩沖區把這個數據輸出到打印機上,讓打印機執行具體的打印任務。
那當這個打印任務完成了之后,這個打印文件表就可以從這個隊列當中刪除了。那之后只要打印機空閑,輸出進程就可以從這個隊列中再取出下一個任務,然后根據這個任務的指示來執行后續的那些打印任務。
所以在采用了SPOOLing技術之后,雖然系統當中只有一台打印機,但是每個進程在提出打印請求的時候,系統都會為這個進程在輸出井當中申請一個緩沖區、一個存儲區。其實這就相當於為這個進程分配了一個邏輯上的設備,所以這樣的話就可以讓每個用戶進程都感覺到似乎都是自己獨占了一個打印機設備一樣,這樣的話就實現了對打印機的共享使用。
所以SPOOLing技術可以把一台物理上的設備虛擬成若干個邏輯上的多台設備。可以把這種獨占式的設備改造成共享設備,這就是SPOOLing技術的一個具體應用。
脫機技術它可以實現數據的預輸入和緩輸出。那在現代的操作系統當中,一般來說使用軟件的方式來模擬實現脫機技術,也就實現了所謂的假脫機技術,也叫SPOOLing技術。那我們需要對假脫機技術當中的輸入井、輸出井、輸入進程、輸出進程等等這些概念和脫機技術當中的那些部件一一地對應起來,來理解和記憶。那最后我們又介紹了SPOOLing技術的一個具體應用,把獨占式的打印機改造成一個共享打印機。
在之前的小節中我們介紹了I/O核心子系統(I/O系統)需要實現哪些功能,那上一小節我們介紹了在用戶層軟件需要實現的SPOOLing技術,從這個小節開始我們會開始介紹設備獨立性軟件這一層需要實現的兩個功能。一個是設備的分配與回收,下一節會講內存緩沖區的管理(緩沖與高速緩存)。
之后會介紹兩種很常用的分配策略,靜態分配與動態分配。再之后我們介紹設備分配的是時候操作系統用來管理這些設備資源需要使用的數據結構,並且介紹設備分配的具體步驟,最后會介紹一種設備分配步驟的改進方法。那我們會按從上至下的順序依次講解。
獨占設備就是一個時間段內只能分配給一個進程使用的那些設備,就比如說咱們很熟悉的打印機。
那共享設備呢就是可以同時分配給多個進程使用的那種設備,比如說像磁盤。但是所謂的這種共享,其實是宏觀上共享,但微觀上各個進程有可能是交替地使用着這個設備的。
那虛擬設備呢,其實就是我們上個小節介紹的,采用SPOOLing技術把獨占設備改造成虛擬的共享設備。在采用了SPOOLing技術之后,我們可以把獨占式的打印機改造成共享打印機,並且把它同時分配給多個進程使用。那這就是我們需要考慮的設備的固有屬性。
但是如果從設備的分配算法這個角度來看的話,可以有很多我們很熟悉的算法來進行設備的分配。比如說先來先服務,或者優先級高者優先,短任務優先等等。那這些算法的具體策略其實通過算法的名字就可以判斷,所以這兒就不再展開贅述。
但是如果從設備分配的安全性這個角度來考慮的話,可以有這樣的兩種分配方式,一種叫安全分配方式。也就是為一個進程分配了設備之后,就一定會把這個進程阻塞,直到這個進程釋放這個I/O設備之后才會把它喚醒讓它繼續往下執行。那我們來考慮一下進程請求打印機打印輸出的例子。如果采用的是安全分配方式的話,那么一個進程在請求使用打印機並且把打印機分配給它之后,這個進程就必須先阻塞等待。雖然按理來說這個進程只需要把數據丟給打印機它其實就不用再管打印機那個設備,它還可以繼續往下執行。但是由於我們采用的是安全分配的這種方式,所以這個進程不得不阻塞,一直到打印結束之后進程才會被再次喚醒,繼續往下執行。
所以采用安全分配方式的話,在一個時間段內,每個進程只能使用一個設備。它的優點呢就是破壞了我們之前學過的“請求和保持”條件,所以不會導致死鎖,所以它才叫安全分配方式。但是缺點呢也很明顯,就是對於一個進程來說它的CPU和I/O設備只能串行地工作,因此系統資源的利用率低,並且進程的執行效率也會降低。
那與這種分配方式相反的,另一種叫做不安全分配方式。就是進程發出I/O請求之后,系統為其分配I/O設備,這個進程不會被阻塞,它可以繼續往下執行。之后還可以發出更多的新的I/O請求。一直到某一個I/O請求得不到滿足的時候才會把這個進程阻塞。那還是以進程請求打印機打印輸出的例子。比方說采用的是不安全分配方式的話,那么一個進程在請求打印機的服務之后,系統會把打印機資源分配給它。但是這個進程把數據丟給打印機之后,它其實就不需要再管打印機的這個打印輸出過程,它可以繼續往下執行。甚至在之后的執行過程中它還可以申請使用別的I/O設備,比如說像掃描儀啊等等。所以如果采用的是不安全分配方式的話,那么一個進程可以同時使用多個設備。這種方式的優點就是效率高,進程的計算任務和I/O任務可以並行地執行。但缺點呢就像這個名字所說的這樣,它不安全,有可能會發生死鎖。那我們在第二章中學過,如果采用的是這種方式的話,可以用死鎖避免也就是銀行家算法來避免系統進入不安全狀態。那這是在設備分配的時候需要考慮的三個角度的問題。
那接下來我們來看兩種我們很熟悉的設備分配方式——靜態分配和動態分配。所謂靜態分配就是指進程運行之前就為其分配它所需要的全部資源,一直到進程運行結束之后才把資源歸還給系統。而如果說進程在運行之前它所需要的全部資源暫時得不到完全的滿足的話,那么這個進程就暫時不能投入運行的。那這也是我們在死鎖那個章節學過的內容。
其實,靜態分配方式就是破壞了“請求和保持”條件,所以采用這種分配方式的話是不會發生死鎖的。那講到這里,希望大家也可以再回憶一下咱們死鎖發生的四個必要條件,分別可以用什么樣的方法破壞這四個必要條件呢。這是大家需要思考和復習的地方。
那與靜態分配方式相對的,另一種分配方式叫做動態分配方式。就是進程的運行過程當中,動態地申請設備資源,並不是剛開始就得到全部的資源。那這兩種方式咱們在死鎖那個小節當中也講過,所以這兒就不再展開。
接下來我們來看一下,設備分配管理當中,需要用到哪些數據結構。那在講這些數據結構之前我們先來復習一下設備、控制器還有通道之間的關系。一個通道可以控制多個控制器,而一個控制器又可以控制多個設備,所以我們可以把它們之間的關系理解為一個樹的這種形狀。那從另外一個角度來說,每一個設備它肯定會有一個它所從屬的控制器,而每一個控制器也肯定會有一個它所從屬的通道。這兒需要注意啊,一個系統中,可能會有多個通道。那由於要控制一個設備,肯定需要找到這個設備的控制器。而要控制一個控制器,肯定又需要找到這個控制器所從屬的通道。所以設備分配管理中的數據結構,需要表示出這種從屬的關系。
那系統會為每一個設備配置一張設備控制表,英文縮寫叫DCT,用於記錄設備的使用情況。
一個設備分配表當中,可能會有這樣的一些常用的字段。
設備類型指的是這個設備到底是打印機,還是掃描儀還是鍵盤等等等等。
而設備標識符其實就是我們之前提到過很多次的所謂的物理設備名,它是這個設備的唯一ID。那系統中各個不同的設備它的設備標識符都是不同的。
那設備狀態這個字段呢又是用來區分這個設備此時是處於忙碌啊、空閑啊還是故障啊等等這些不同的狀態的。
而指向控制器表的指針這個就是咱們剛才提到的,用於找到一個設備它所從屬的控制器到底是哪一個。
重復執行次數或時間。這個字段是用來干什么的呢?一個設備有很多機械零件,所以設備故障的幾率其實是很大的。比如說使用打印機或者復印機的時候經常會出現卡紙的現象,不過並不是說執行一次I/O操作失敗之后就認為這次I/O操作真的失敗了。系統會重復執行多次這個I/O操作。那這個字段呢就是用於記錄它到底失敗了幾次。只有重復執行多次之后仍然不成功,那才認為這次I/O是失敗的。
最后一個字段——設備隊列的隊首指針。這個字段其實就是用於指向此時正在等待着使用這個設備的進程隊列的。那這個隊列由進程的PCB組成。
還記不記得咱們在“進程管理”章節當中曾經提到過,系統會根據各個進程阻塞原因的不同,將進程的PCB掛到不同的阻塞隊列當中。那假如說我們的進程此時正在等待某一個I/O設備的分配,而這個I/O設備又暫時沒法分配給它的話,那么這個進程就會掛到那個I/O設備設備控制表所指向的這個等待隊列的隊尾。所以,這個隊列指針其實指向的就是相應的阻塞隊列。那這是系統中需要配置的第一種數據結構——設備控制表(DCT)。
第二種數據結構叫做控制器控制表,英文縮寫是COCT。每一個設備控制器都會對應一張設備控制表。系統會根據這個設備控制表當中的信息對設備控制器進行控制和管理。那這個表當中也會有一些很常用的字段。
比如說控制器的標識符,那這個就是各個控制器的唯一ID。
另外還需要記錄設備控制器的狀態,比如說忙碌還是空閑。
再者呢,每個設備控制器都會有一個它所從屬的通道,所以在設備控制器的控制表當中,也會有一個指向通道表的指針。系統可以根據這個指針找到它所從屬的通道。
那最后還需要有一個隊首指針和隊尾指針。這個隊列其實就是指向了正在等待這個設備控制器的進程。那這是需要配置的第二個數據結構——控制器控制表。
第三個數據結構是通道控制表,英文縮寫是CHCT。每個通道都會對應一張通道控制表。當操作系統會根據這個表的信息對通道進行相應的操作和管理。
那與之前的兩個數據結構很類似,通道標識符指的是這個通道的唯一ID。
通道狀態就是指明它是忙碌、空閑還是故障。
而第三個與通道連接的控制器表的首址。操作系統可以根據這個字段找到這個通道控制的所有的控制器相關的信息,也就是找到這些控制器控制器表(COCT)的集合。
那如果說通道此時暫時不能為一個進程服務,那么這個進程一樣是需要等待這個通道的服務。所以最后的這兩個字段就是用於指向等待這個通道的那些進程隊列的。那這是通道控制表的一個作用。
那第四個需要設置的數據結構叫系統設備表,英文縮寫是SDT。這個表當中記錄了系統中全部設備的情況。
每一個設備會對應一個表目。那每個表目中又會記錄這個表目所對應的設備的設備類型。比如說,它是打印機還是掃描儀還是鍵盤。又會記錄這個設備的標識符,也就是它的物理設備名,唯一的ID。那當然這個表目中還包含着這個設備的控制表DCT。最后,還記錄了這個設備的驅動程序的入口地址。再次強調,系統設備表當中記錄的是系統中全部設備的情況。所以其實當用戶用設備名請求使用某一個設備的時候,操作系統是可以從這個系統控制表當中,來找到用戶指定的設備到底是哪一個的。
那接下來我們來分析一下設備分配的具體步驟。那用戶在編程的時候需要提供物理設備名作為這個系統調用的參數。
那操作系統用某種方式來查找系統控制表的時候就會把各個表目當中記錄的這個設備標識符和用戶提供的物理設備名進行比對,然后找到這兩個參數相匹配的一個表項,之后就可以找到這個設備對應的設備控制表DCT了。
於是接下來會根據DCT當中記錄的信息,來判斷此時這個設備是否空閑。如果設備空閑的話,就可以把這個設備分配給那個進程。而如果這個設備此時是忙碌的話,那么就需要把這個進程掛到這個設備對應的等待隊列、阻塞隊列當中。一直到這個設備空閑,並且把設備分配給該進程之后才會把這個進程重新喚醒。那除了分配這個設備之外,還需要把這個設備對應的控制器也分配給這個進程。所以系統會根據指向控制器表的指針這個字段找到這個設備對應的控制器控制表,也就是COCT。
那與剛才類似,首先會檢查這個控制器此時是不是空閑。如果空閑的話,可以把這個控制器分配給這個進程。如果控制器忙碌,那就需要把這個進程放到控制器的阻塞隊列當中。那分配了控制器之后,還需要給這個進程分配相應的通道。於是又會根據這個指針(控制器隊列的隊首指針和控制器隊列的隊尾指針),找到相應的通道控制表,也就是CHCT。
這個過程也和剛才類似,首先會判斷通道的狀態,如果通道空閑的話,那么可以把通道分配這個進程。如果通道忙碌的話,就需要把進程掛到通道的阻塞隊列當中。
那只有設備、控制器和通道這三個東西都分配成功的時候,這次設備分配才算成功。之后就可以開始啟動I/O設備進行數據傳送了。那這是設備分配的一個步驟。
那我們來思考一下,這個分配過程有什么缺點呢?
還記不記得我們剛才說的,采用這種分配方式的話,用戶在編程的時候其實是需要提供物理設備名作為系統調用的參數的,所以這種方式對用戶編程來說是很不方便的。假如說用戶使用的物理設備名叫A,而之后這個物理設備又進行了更換,把物理設備A換成了物理設備B。但是由於用戶在編程的時候使用的依然是A這個物理設備名,因此只要物理設備更換了,那么用戶之前所寫的程序就無法運行了。它在請求使用物理設備A的時候肯定是沒辦法給它分配成功的。那除了這個缺點之外,當一個進程它請求的物理設備正在忙碌的時候,即使系統當中還有同類型的設備,但這個進程依然是必須阻塞等待的。比如說我們一台電腦上連了三台打印機,那如果說這個進程請求使用的是第一台打印機,那么雖然第二台和第三台打印機此時有可能是空閑的,但只要第一台打印機此時是忙碌的,那這個進程依然是需要阻塞等待。但顯然其實可以把進程的這個打印任務把它分配給第二台或者第三台打印機進行。因此,采用這種方式的話也會導致設備的利用率不高的問題。
那解決這個問題的方法,其實咱們在之前談到過,就是可以建立邏輯設備名與物理設備名之間的映射機制。用戶編程的時候使用的是邏輯設備名,然后由操作系統來負責完成邏輯設備名到物理設備名的轉換。
那引入這種機制之后,進程在請求使用某種設備的時候,只需要提供邏輯設備名。所謂的邏輯設備名,其實就是要指明它所使用的設備類型。
比如說它想使用的是打印機這種類型的設備。
那由於系統設備表(SDT)當中有一個字段它就是記錄的設備的類型。因此操作系統可以根據用戶提供的邏輯設備名也就是設備類型來依次查找這個系統設備表(SDT),然后找到一個指定類型的,並且空閑的設備把它分配給進程。那只有這個類型的設備全部都處於忙碌狀態的時候,才需要把這個進程阻塞。
那把這個設備分配給進程之后,操作系統還需要在邏輯設備表(LUT)當中新增一個表項。那邏輯設備表(LUT)就是用於記錄邏輯設備名到物理設備名的一個映射關系的一張表。除了這個映射關系之外,還需要記錄這個設備對應的驅動程序的入口地址。
那之后的操作就和之前一樣了。可以根據這個設備控制表(DCT)找到相應的控制器控制表(COCT),然后把控制器分配給設備。之后又再根據控制器控制表(COCT),找到通道控制表(CHCT),把通道分配給設備。那只有進程第一次通過邏輯設備名申請使用一個設備的時候,操作系統才會來查詢這個系統設備表。
如果之后進程再次以相同的邏輯設備名來請求使用設備的話,那么操作系統首先做的是是會在這個邏輯設備表(LUT)當中查找這個邏輯設備對應的物理設備,然后找到相應的表項之后,就可以找到這個設備對應的驅動程序了。
那有這樣的兩種方式設置邏輯設備表。第一種就是整個系統中只有一張邏輯設備表。這樣的話各個用戶所使用的邏輯設備名是不允許重復的,所以這種方式只適合用於單用戶操作系統。第二種的話就是每個用戶設置一張邏輯設備表。那這樣的話不同用戶的邏輯設備名可以重復,它可以適用於多用戶的操作系統。那這一點咱們在之前的小節當中也提到過。
好的那個這個小節我們介紹了設備的分配與回收問題。主要介紹的是設備的分配。其實設備的回收無非就是把那些數據結構再把它改回來就行了。那在設備分配的時候我們應該考慮這樣的三種因素。
其中不太容易記憶的是安全分配方式和不安全分配方式。大家在復習到相應概念的時候,需要留個心眼,在選擇題當中有可能進行考查。
另外靜態分配和動態分配這兩個術語要有印象。其實靜態分配方式就是破壞了“請求和保持”這一個產生死鎖的必要條件,而動態分配方式呢效率更高,但是有可能產生死鎖。那一般來說我們可以利用銀行家算法來避免死鎖或者結合資源分配圖來對死鎖進行檢測和解除。那銀行家算法的規則還有死鎖的檢測和解除這也是大家需要回顧的知識點。
那我們之后還介紹了設備分配管理當中所需要用到的數據結構。那這些數據結構其實並不需要死記硬背。設備控制表、控制器控制表、通道控制表它們分別對應設備、控制器還有通道這三種東西,那其實我們只需要理解這三種東西的一個對應關系我們就可以知道這些數據結構它們大致是需要做什么事情的了。一個通道會控制多個控制器,一個控制器又會控制多個設備,所以它們之間的關系是一種樹型的關系。
而無論是設備還是控制器還是通道它們都會有一個字段叫做等待隊列的指針。其實就是當這種資源暫時不能分配給某個進程的時候,需要把這個進程的PCB插入到相應資源的等待隊列當中。那系統設備表呢集中了所有的設備相關的信息,操作系統可以通過這個表來統一地對設備進行查詢檢索。
那基於這些數據結構,我們介紹了設備分配的具體步驟。我們需要知道的是,如果采用的是物理設備名來請求使用一個設備的話,它會存在一些很明顯的缺點。並且這些缺點可以用邏輯設備名和物理設備名映射的這種方式來解決。那這個地方是大家需要着重理解的點,很有可能在選擇題當中進行考查。
這門課的最后一個知識點——緩沖區管理。
那我們會首先介紹什么是緩沖區,緩沖區有什么作用,之后會介紹幾種常見的、常考的緩沖區的解決方案。
像咱們之前學到過的聯想寄存器,也就是快表,它就是一種由硬件來實現的緩沖區。那由於對快表的這個訪問頻率是很高的,所以如果快表的速度高的話,那么整個系統的性能提升會很明顯。因此在這種情況下,使用這種成本高的解決方案是值得的。
但一般情況下更多的是用內存作為緩沖區。那像設備獨立性軟件這一層的緩沖區管理主要其實指的是對內存作為緩沖區的這種這種緩沖區進行組織和管理。
所以我們這一小節介紹的所有的這些緩沖區,主要還是圍繞內存作為緩沖區這種類型。
首先來看一下緩沖區有什么作用。
在內存中可以開辟一小片區域作為緩沖區,
那么如果要輸出數據的話,那么CPU產生的這些數據首先會被放到內存的緩沖區當中。不過CPU的速度很快,所以它很快就可以把這個緩沖區給充滿。那當緩沖區放滿了之后,CPU就可以轉頭去做別的事情。
之后I/O設備可以慢慢地從這個緩沖區當中取走數據。
而在數據輸入的時候其實也是類似的。I/O設備可以慢慢地把數據放到緩沖區當中,當緩沖區滿了之后,CPU再很快速地從緩沖區中取走數據。所以采用這種方式的話,很明顯可以緩和CPU和I/O設備之間的速度不匹配的矛盾。
那如果說沒有采用緩沖區這種策略的話,I/O設備每輸入或者每輸出一定單位的數據之后,就需要對CPU發出一個中斷信號請求CPU介入處理。那假設這個I/O設備是一種字符型的設備的話,那每輸入完一個字符或者每輸出一個字符I/O設備都會打斷CPU向CPU發出中斷信號。而之前咱們強調過,對中斷的處理是需要付出一定的時間代價的,因此CPU頻繁地處理這些中斷顯然會降低系統的性能。而如果采用采用緩沖區這種策略的話,只有緩沖區中的數據被全部取走了,或者輸入的數據充滿了緩沖區的時候,CPU才需要介入來處理中斷。因此,采用這種方式可以減少CPU的中斷頻率,放寬CPU對中斷響應時間的限制。
那緩沖區還有一個作用就是解決數據粒度不匹配的問題。比如說此時在CPU上運行的輸出進程,每次可以生成一整塊的數據。但是I/O進程每次只能輸出一個字符,那如果沒有采用緩沖區策略的話,輸出進程只能一個字符一個字符地給這個I/O設備來傳輸數據。但如果采用的緩沖區這種策略的話,輸出進程可以直接把一整塊的數據放到緩沖區里,讓I/O設備從這個緩沖區當中一個字符一個字符地往外取。那在輸入的時候其實也是類似的,緩沖區同樣可以解決這種數據粒度不匹配的問題。那另外呢,采用了緩沖區之后,很顯然是可以提高CPU和I/O設備之間的並行性的。那接下來我們介紹幾種我們需要掌握的緩沖區管理的策略。
首先來看一下什么是單緩沖。而這個緩沖區的大小和塊是相等的。那我們首先來看一下對一個數據塊的處理需要經過哪些步驟。首先系統在主存當中為這個用戶進程分配了一塊大小的緩沖區。
那么這個塊設備會產生一塊大小的數據,
把它輸入到這個緩沖區當中。
這個過程我們假設所耗費的時間為T。
那之后呢這塊數據需要傳送到用戶進程的工作區當中才可以被用戶進程所處理的。還記得我們之前小節中提到的例子嗎?C語言的scanf這個函數讀入一個數字的時候,其實我們從鍵盤輸入的這個數據最終是要被傳送到用戶進程的內存空間當中的。
所以在這個地方,數據塊,被傳入了緩沖區還不夠,它還需要把這個數據塊把它傳送到用戶進程的工作區當中。那同樣的,在考研當中,我們默認用戶進程的工作區也是剛好可以放得下這樣一塊數據。
所以接下來會用M這么長的時間,把緩沖區當中的數據傳送到用戶進程的工作區中。
那之后用戶進程就可以開始對這一塊數據進行處理。那我們假設對一塊數據進行計算處理所需要的時間為C這么多。那當它處理完之后,這個用戶進程的工作區就可以被騰空了。
那我們在考研當中經常考查的題型是,會讓我們計算每處理一塊數據平均需要多長的時間。
那咱們的書上給出了一個很好用的技巧。
那來看第一種情況。假設輸入時間T>處理時間C。那根據我們假設的條件,剛開始工作區是滿的,緩沖區是空的。所以剛開始,CPU就可以處理工作區中的這塊數據了。
那這個處理的過程,需要花C這么長的時間。
另外呢,由於剛開始緩沖區就是空的,而我們剛才說過,只要緩沖區空的話,那其實是可以往緩沖區當中充入數據的。
所以剛開始塊設備就往緩沖區當中充入一塊數據,那這個時間總共是耗費了T這么久。那把緩沖區充滿總共需要花費T這么長的時間。
那由於這個數據充入的時間大於處理數據的時間,因此CPU在花了C這么長的時間處理完數據之后,它並不可以緊接着繼續處理下一塊的數據。因為當它處理完這個數據之后,其實緩沖區當中的數據是還暫時沒有充滿的,因此必須先等到緩沖區的數據充滿並且把這些數據傳送到工作區之后才可以進行下一塊數據的處理。那顯然在T這個時間點,緩沖區充滿了。
接下來可以緊接着把這一塊數據傳送到工作區當中,這個過程又需要花費M這么長的時間。還記得我們剛才假設的初始狀態嗎?工作區滿,緩沖區空。
其實在經歷T+M這么長的時間之后,就再一次回到了我們剛剛假設的這種初始狀態,也就是工作區滿,緩沖區空。
那接下來的數據處理其實無非也就是重復剛才我們分析的這個過程。
因此,通過剛才的分析我們發現,平均每處理一塊數據需要花的時間是T+M這么多。
那再來看第二種情況,假設輸入一塊數據的時間要小於處理一塊數據的時間的話,
那么由於剛開始緩沖區就是空的,
所以從0這個時刻塊設備就可以開始往緩沖區當中充入數據,那這個過程需要花費T這么長的時間,可以把緩沖區充滿。
同時呢,由於剛開始工作區是滿的,所以CPU可以在0這個時刻就開始處理這塊數據。但這一塊數據處理的時間需要C這么長,而C是大於T的。因此雖然緩沖區當中的數據充滿了,
但是由於工作區中的這些數據暫時還沒有處理完,所以在T這個時刻,並不可以直接把緩沖區中的數據把它繼續傳送到工作區當中。
必須等到工作區中的這塊數據處理完了,也就是到C這個時刻,才可以開始把這塊數據傳送到工作區當中。
所以接下來這個傳送的過程需要耗費M這么長的時間。那到這一步,又回到了我們剛才假設的這種初始狀態,工作區是滿的,緩沖區是空的。
所以這個過程總共耗費了C+M這么長的時間。那經過M這么長的時間之后,緩沖區的數據就全部被取空了。
因此接下來塊設備又可以開始往里邊輸入數據,並且CPU也可以開始處理工作區當中的這塊數據。
那接下來的過程其實無非也就是在重復我們剛才所說的這個過程。
因此可以發現,如果T<C的話,那么每處理一塊數據的平均用時應該是C+M這么多。
所以經過剛才的分析我們得到一個結論,如果采用的是單緩沖區的策略的話,那么每處理一塊數據,平均需要耗時C和T當中更大的那個再加M這么多的時間,這是對單緩沖策略的一個分析過程。
那接下來我們再來看雙緩沖策略。如果采用的是雙緩沖策略的話,操作系統會在主存當中為進程分配兩個緩沖區。那同樣的,每個緩沖區的大小也是一個塊。
類似的,雙緩沖問題也經常會考查每處理一塊數據平均需要耗時多久這樣一個問題。那我們和剛才的思路是類似的,我們可以假設一個初始狀態,然后分析一下下次到達這個初始狀態總共需要耗時多少。我們假設剛開始工作區是空的,其中一個緩沖區是滿的,而另一個緩沖區是空的。
那先來看第一種情況。假設T>C+M,那根據我們假設的初始狀態,緩沖區1當中此時是有1塊數據的,緩沖區2是空的,工作區也是空的。
所以在0這個時刻,可以把緩沖區1當中的數據傳送到工作區當中。那這個過程耗時應該是M這么多。
而接下來CPU就可以開始處理工作區當中的這個數據,耗時是C這么多。
另一方面,在剛開始緩沖區2就是空的,
所以剛開始塊設備就可以往緩沖區2當中輸入數據。
那充滿這個數據總共需要耗時T這么多。那由於T>C+M,所以雖然在這個時間點CPU已經把工作區當中的這塊數據處理完了,工作區已經空了,但是由於緩沖區2此時這個時刻還沒有充滿,所以暫時不能把緩沖區2當中的數據把它傳送到工作區當中,必須等到T這個時間點,緩沖區2才可以被充滿。所以到T這個時刻,其實就回到了我們假設的那種工作狀態。工作區此時是空的,而這兩個緩沖區當中,其中一個是空的,另一個是滿的。
因此當T>C+M的時候,處理一塊數據平均用時應該是T這么多。
而接下來無非就是再對剛才咱們分析的這一系列過程再進行重復。那整體來看,每處理完一個數據塊,耗時都是T這么多。
那接下來再來看第二種情況,T<C+M。
剛開始緩沖區2是空的,
所以剛開始塊設備可以往緩沖區2當中充入數據,耗時是T秒這么多。
另一方面,由於剛開始緩沖區1當中是充滿數據的,
所以剛開始就可以把緩沖區1當中的數據傳送到工作區當中,到M這個時刻工作區會被充滿,然后CPU就可以開始處理這些數據,處理數據總共耗時C這么長。那處理完工作區當中的這塊數據之后,此時緩沖區2當中其實也已經充滿了下一塊的數據了。
因此接下來可以緊接着把緩沖區2當中的數據傳送到工作區當中,接着繼續處理工作區中的這塊數據。
那我們再回頭來看數據輸入的這個過程,在T這個時刻緩沖區2已經被充滿,然后設備開始空閑,並且由於緩沖區1當中的數據在M這個時刻就已經被取空了,因此緩沖區2的數據被充滿了之后,設備就可以緊接着往緩沖區1當中充入數據,這個耗時也是T這么多。
那我們假設在這個時間點,緩沖區2當中的數據還沒有完全被取走,所以在這個時間點這兒,雖然設備空閑了,但是由於緩沖區2還沒有被取空,而緩沖區1又剛剛被充滿的,因此在這個時間點,設備並不能緊接着往緩沖區2當中充入下一塊數據。
那只有緩沖區2當中的數據被取空之后,這個設備才可以繼續往緩沖區2當中寫入下一塊的數據。所以其實這樣一步一步分析下來大家會發現,如果采用的是雙緩沖結構並且T<C+M的話,那么我們很難找到一個和剛開始的這種初始狀態一模一樣的這種狀態。比如說像T這個時刻,雖然其中緩沖區2是滿的,但是由於此時工作區當中的數據還沒有全部被處理完,因此工作區又不是空的,這和我們剛開始的初始狀態是不一樣的。而在M+C這個時刻,雖然我們的工作區是空的,但是緩沖區2當中的數據是滿的,並且緩沖區1當中也充入了一部分數據。也就是說在M+C這個時刻,工作區是空的,然后一個緩沖區是滿的,另一個緩沖區是半滿的。所以如果T<C+M的話,那課本當中介紹的這種方法其實就不太好使了。所以自己用這種甘特圖的方式多往下分析幾次就會發現,其實每經過M+C這么長的時間,就會有一塊數據被處理完畢。
因此當T<C+M的時候,每處理完一個數據塊,平時耗時應該是C+M這么多。
所以經過剛才的這兩種情況的分析我們發現,如果T>M+C的話,那么平均處理一個數據塊需要T這么長的時間。而如果T<M+C的話,那么平均處理一個數據塊需要的是M+C這么長的時間。
那像單緩沖和雙緩沖策略,其實不僅僅是在主機和設備之間的這種數據傳送當中可以使用。
其實在兩台主機通信時,也可以采用這種緩沖的策略,而用於數據的發送和接收。
那如果說為兩台通信的這個主機配置單緩沖區的話,
那A主機想要發送數據,首先需要把這些數據放入到自己的這個緩沖區當中,
等緩沖區充滿之后就可以往B主機的緩沖區當中一個一個地發送數據。
那之后B主機可以開始處理這些數據。
等這些數據處理完之后,這個緩沖區才會變空。只有等這些數據全部被取走,全部處理完之后,那B主機的緩沖區才可以被騰空。而這個緩沖區中的數據被全部取走之后,B主機要向A主機發送的數據才可以開始放入到這個緩沖區當中。
所以如果兩台通信的機器只設置單緩沖區的話,那么在任何一個時刻都只能實現數據的單向傳輸。那這其中的原因呢,其實就是咱們之前強調過的緩沖區的特性決定的。緩沖區只有充滿之后才可以取走數據,而只有取空之后才可以往里邊放入數據。
那為了實現同一時刻雙向傳輸,
我們可以給兩台機器配置雙緩沖區,其中一個緩沖區用來暫存即將發送的那些數據,而另一個緩沖區用於接收輸入的數據。
所以如果采用雙緩沖的結構的話,那么這兩台主機都可以同時往自己的發送緩沖區當中充入自己想要發送出去的那個數據,
接下來可以同時往對方的接收緩沖區當中充入數據。那這樣的話就實現了同一時刻雙向傳輸的功能。
那講到這個地方,大家可以再回憶一下咱們在進程通信那個小節當中提到過的管道通信這種方式。有很多同學都有疑問說,為什么管道通信當中只有管道充滿的時候才可以取出數,只有管道取空的時候才可以往管道里放入數據。其實管道通信當中的管道它就是一種緩沖區,而由緩沖區的特性就決定了管道通信就應該是那樣的。所以在管道通信中我們也強調過,如果要實現雙向傳輸的話,必須設置兩個管道,也就是設置兩個緩沖區。那這是緩沖區這一塊的知識點和管道通信的一個聯系。
接下來我們再來學習下一種常見的緩沖區,叫做循環緩沖區。很多時候只有兩個緩沖區依然不能滿足進程的實際需要,
所以操作系統可以給一個進程分配多個大小相等的緩沖區,讓這些緩沖區連成一個循環的隊列。那在這個圖當中橙色表示的是已經被充滿數據的緩沖區,而綠色表示的是此時為空的緩沖區。
那系統會保持兩個指針用於緩沖區的管理。in指針需要指向下一個可以充入數據的空緩沖區,而out指針可以指向下一個可以取出數據的滿的緩沖區。那當這個緩沖區的數據被取空之后,out這個指針可以開始指向下一個緩沖區了。類似的,如果in這個指針所指向的緩沖區被充滿之后,那么in指針也需要指向下一個空的緩沖區。那這是循環緩沖區的方式。那一般來說只有單緩沖和雙緩沖需要我們分析處理一塊數據平均所需要消耗的時間。循環緩沖區我們只需要了解一個它的大致原理就可以了。
接下來我們再來看一個不太容易理解的緩沖池這種方案。緩沖池其實是由一系列的緩沖區組成的,就像它這個名字一樣。緩沖池其實就是放滿了各種各樣緩沖區的一個池子,當我們需要使用一個緩沖區或者要歸還一個緩沖區的時候,就可以對這個池子當中的各種緩沖區進行操作就可以了。
所以緩沖池當中的緩沖區可以按照使用狀況分為空緩沖隊列,還有裝滿了輸入數據的輸入隊列,還有裝滿了輸出數據的輸出隊列這樣三個隊列組成。
另外呢根據一個緩沖區在實際運算當中所扮演的功能不同,又設置了四種工作緩沖區。
一個是用於收容輸入數據的工作緩沖區,還有用於提取輸入數據的工作緩沖區,還有用於收容輸出數據的工作緩沖區,和用於提取輸出數據的工作緩沖區。這些名詞都很奇怪並且很拗口,不過其實它們的原理並不復雜。
如果一個輸入進程要請求輸入一塊數據的話,
那么系統會從空緩存隊列的隊頭當中取下這一塊空的緩沖區,
把它作為用於收容輸入數據的緩沖區。
那當這塊緩沖區被充滿之后,
就會被掛到輸入隊列的隊尾上。
那如果計算進程想要取得一塊之前已經輸入了的數據的話,
那么操作系統會從輸入隊列的隊頭取下一個緩沖區,把它作為提取輸入的工作緩沖區。
那接下來這塊緩沖區當中的數據會被傳送到計算進程的工作區當中。所以這塊緩沖區當中的數據就被取空了,那當它取空了之后,
這個空緩沖區又會被掛回到空緩沖隊列的隊尾。
第三種情況,如果此時計算進程已經准備好了數據,想要把這些數據充入到緩沖區的話,
那么系統會從空緩沖隊列的隊列的隊頭取下一個空閑的緩沖區,把這個緩沖區作為收容這個進程想要輸出數據的工作緩沖區。
因此接下來這個緩沖區慢慢地會被充滿,那由於這塊緩沖區當中的數據接下來是要輸出到I/O設備上的,
所以這塊數據會被掛到輸出數據的隊尾。
第四種操作,如果某個輸出進程請求輸出一塊數據的話,
那么系統會從輸出隊列的隊頭取下一塊緩沖區,把它作為提取輸出數據的工作緩沖區。
接下來這塊緩沖區當中的輸出數據會被慢慢取走,
當緩沖區被取空之后,就可以把這個緩沖區掛回到空緩沖區隊列的隊尾了。所以這是緩沖池相關的幾種操作和原理。
介紹了緩沖區管理相關的知識點。我們需要理解緩沖區的各種優點,需要對這些優點有一些大體的印象,可以應付選擇題。另外呢,大家需要重點重點注意的是,單緩沖和雙緩沖結構當中,每處理一塊數據平均需要耗時多少。那這種題型是經常在選擇題當中出現的。那最后我們還介紹了循環緩沖和緩沖池,對於這兩種緩沖管理方案來說,我們並不需要去研究太深的那些細節,我們只需要對它們的原理有一個大體的印象,理解就可以了。