作業系統
清華大學ucore作業系統課筆記
全文思維導圖
1. 作業系統概述
1.1 什麼是作業系統?
作業系統的定義
沒有公認的精確定義
-
一個控制程式
- 一個系統軟體
- 控制程式執行過程,防止錯誤和計算機的不當使用
- 執行使用者程式,給使用者程式提供各種服務
- 方便使用者使用計算機系統
-
一個資源管理器
- 應用程式和硬體之間的中間層
- 管理各種計算機軟硬體資源
- 提供訪問計算機軟體硬體資源的高效手段
- 解決資源訪問衝突,保證資源公平使用
作業系統的地位
作業系統軟體的組成
- shell--命令列介面
- GUI--影像使用者介面
- Kernel--系統內部
- 執行各種資源管理的功能
ucore教學作業系統核心
組成
作業系統核心特徵
-
併發
計算機同時存在多個執行的程式,需要OS管理和排程
-
共享
同時”訪問“,互斥共享
-
虛擬
利用多道程式設計技術,讓每個使用者都覺得有一個計算機為他服務
-
非同步
程式是走走停停的,推進速度不可知
只要執行環境相同,OS要確保程式執行結果的一致性
1.2 作業系統例項
三大家族
UNIX BSD
Linux
Windows
1.3 作業系統的演變
單使用者系統
時間:1945-1955
作業系統=裝載器+通用子程式庫
問題:昂貴元件的低利用率
批處理系統
時間:1955-1965
順序執行與批處理
多道程式系統
時間:1965-1980
保持多個工作在記憶體中並且在各個工作之間複用CPU,由順序執行變成了多道程式的交替執行
多道系統只是為了讓CPU一直處於工作狀態,當目前程式出現I/O請求即暫時不再使用CPU時,CPU才去執行另一道程式。多道批處理系統的目的是為了解決人機矛盾及CPU和I/O裝置速度不匹配的矛盾,提高系統的有效性(包括資源利用率和吞吐量),並不提供人機互動能力。
分時系統
時間:1970-
定時中斷用於工作對CPU的複用
程式執行公平性更好,提高短作業的速度
分時系統中會將處理器的時間分成短的時間片,定時會切換不同的程式執行。這與多道批處理系統有本質上的區別。
分時系統是實現人機互動的系統。
個人電腦作業系統
個人計算機:每個使用者一個系統
-
單使用者
-
利用率不再是關注點
-
重點是使用者介面和多媒體功能
-
很多舊的服務和功能不再存在
分散式作業系統
分散式計算機:每個使用者多個系統
-
網路支援成為一個重要的功能
-
支援分散式服務
- 跨多系統的資料共享和協調
-
可能使用多個處理器
- 鬆、緊耦合系統
-
高可用性與可靠性的要求
計算機系統的演變
1.4 作業系統的結構
簡單結構
MS-DOS
在最小的空間,設計用於提供大部分功能(1981-1994)
-
沒有拆分模組
-
雖然MS-DOS在介面和功能水平沒有很好地分離,主要用匯編彙編
-
每一種硬體對應一種作業系統,沒有通用的
分層結構
-
作業系統分為很多層
- 每層建立在低層之上
- 最底層是硬體
- 最高層是使用者介面
-
每一層僅使用更低一層的功能(操作)和服務
UNIX作業系統與C語言
-
設計用於UNIX作業系統的編碼例程
-
”高階“系統程式語言建立可移植作業系統的概念
將作業系統的程式碼分為兩部分:一部分與硬體平臺無關的部分(高階語言),特定硬體平臺相關的部分(組合語言)。實現作業系統的可移植性。
ucore也是分層結構,本課程涉及到的部分(紅線部分)
分層結構層次越來越複雜,會導致效率的下降
微核心結構(Microkernel)
儘可能把核心功能移到使用者空間
使用者模組間的通訊使用訊息傳遞
好處:靈活安全
缺點:效能下降
外核結構(Exokernel)
讓核心分配機器的物理資源給多個應用程式,並讓每個程式決定如何處理這些資源。
(原來作業系統的功能是由使用者態的函式庫來提供)
程式能連結到作業系統庫(libOS)實現作業系統抽象
保護與控制分離
VMM(虛擬機器管理器)
負責把真實的硬體虛擬成若干個虛擬的硬體,虛擬機器管理器決定每個虛擬機器可以使用哪些硬體資源
2. 啟動、中斷、異常和系統呼叫
2.1 啟動
BIOS
- 計算機體系結構概述
- 計算機記憶體和硬碟佈局
啟動時計算機記憶體和磁碟佈局
ROM只讀儲存
真實模式只有20位,地址空間為1mb
載入程式的記憶體地址空間
BIOS系統呼叫
BIOS以中斷呼叫的方式,提供了基本的I/O功能
- INT 10h: 字元顯示
- INT 13h: 磁碟扇區讀寫
- INT 15h: 檢測記憶體大小
- INT 16h: 鍵盤輸入
只能在x86的真實模式下訪問
系統啟動流程
CPU初始化
- CPU加電穩定後從0XFFFF0讀第一條指令
- CS:IP = 0xf000:fff0
- 第一條指令是跳轉指令
- CPU初始狀態為16位真實模式
- CS:IP是16位暫存器
- 指令指標PC = 16*CS +IP
- 最大地址空間是1MB
BIOS初始化
- 硬體自檢POST
- 檢測系統中記憶體和顯示卡等關鍵部件的存在和工作狀態
- 查詢並執行顯示卡等介面卡BIOS,進行設定初始化
- 執行系統BIOS,進行系統檢測
- 檢測和配置系統中安裝的即插即用裝置
- 更新CMOS中的擴充套件系統配置資料表ESCD(改寫硬體配置表)
- 按指定啟動順序從軟盤、硬碟或光碟機啟動(交出控制權)
主開機記錄MBR格式
為了解決多分割槽啟動問題,選擇其中一個分割槽啟動
最多四個分割槽
- 啟動程式碼:446位元組
- 檢查分割槽表正確性
- 載入並跳轉到磁碟上的載入程式
- 硬碟分割槽表:64位元組
- 描述分割槽狀態和位置
- 每個分割槽描述資訊佔據16位元組
- 結束標誌:2位元組(55AA)
- 主開機記錄的有效標誌
分割槽引導扇區格式
- 跳轉指令:跳轉到啟動程式碼
- 與平臺相關程式碼
- 檔案卷頭:檔案系統描述資訊
- 啟動程式碼:跳轉到載入程式 (放在硬碟上的,可以改)
- 結束標註:55AA
載入程式(BootLoader)
系統啟動規範
BIOS
- 固化到計算機主機板上的程式
- 包括系統設定、自檢程式和系統自啟動程式
- BIOS-MBR(最多四個分割槽)、BIOS-GPT(全域性分割槽表大於4個)、PXE(網路啟動)
UEFI
統一可擴充套件固定介面
可信度檢查
- 介面標準
- 在所有平臺上一致的作業系統啟動服務
2.2 中斷、異常和系統呼叫
為什麼需要中斷、異常和系統呼叫?
-
在計算機執行中,核心是被信任的第三方
-
只有核心可以執行特權指令
-
方便應用程式
中斷和異常希望解決的問題
- 當外設連線計算機時,會出現什麼現象?
- 當應用程式處理意想不到的行為時,會出現什麼現象?
系統呼叫希望解決的問題
- 使用者應用程式是如何得到系統服務?
- 系統呼叫和功能呼叫的不同之處是是什麼?
核心的進入與退出
定義
系統呼叫(system recall)
- 應用程式主動向作業系統發出的服務請求
異常(exception)
- 非法指令或者其他原因導致當前指令執行失敗(如記憶體出錯)後的處理請求
中斷(hardware interrupt)
- 來自硬體裝置的處理請求
三者比較
源頭
- 中斷:外設
- 異常:應用程式意想不到的行為(核心程式碼也可能出現問題)
- 系統呼叫:應用程式請求提供操作服務
響應方式
- 中斷:非同步(不會被感知)
- 異常:同步(必須處理異常)
- 系統呼叫:非同步或同步
處理機制
- 中斷:持續,對使用者應用程式是透明的
- 異常:殺死或重新執行意想不到的應用程式指令
- 系統呼叫:等待或持續
中斷處理機制
這裡的中斷是指三種形式的總稱
硬體處理
- 在CPU初始化時設定中斷使能標誌(初始化時不進行處理)
- 依據內部或外部事件設定中斷標誌
- 依據中斷向量呼叫相應中斷服務例程
核心軟體
- 現場保護(編譯器)
- 中斷服務處理(服務例程)
- 清楚中斷標記(服務例程)
- 現場恢復(編譯器)
中斷巢狀
硬體中斷服務例程可被打斷
- 不同硬體中斷源可能硬體中斷處理時出現
- 硬體中斷服務例程中需要臨時禁止中斷請求
- 中斷請求會保持到CPU做出響應
異常服務例程可被打斷
- 異常服務例程執行時可能出現硬體中斷
異常服務例程可巢狀
- 異常服務例程可能出現缺頁
系統呼叫
-
作業系統服務的編譯介面
-
通常由高階語言編寫(c/c++)
-
程式訪問通常是通過高層次的API介面而不是直接進行系統呼叫
-
常見的應用程式程式設計介面(API)
Win32 API
用於windows
POSIX API
用於POSIX-based systems
(包括UNIX
,LINUX
,Mac OS X
的所有版本)Java API
用於JAVA
虛擬機器(JVM
)
系統呼叫的實現
-
每個系統呼叫對應一個系統呼叫號
- 系統呼叫介面根據系統呼叫號來維護表的索引
-
系統呼叫介面呼叫核心態中的系統呼叫功能實現,並返回系統呼叫的狀態和結構
-
使用者不需要知道系統呼叫的實現
- 需要設定呼叫引數和獲取返回結果
- 作業系統介面的細節大部分都隱藏在應用程式設計介面後
- 通過執行程式支援的庫來管理
函式呼叫和系統呼叫的不同之處
- 系統呼叫
INT
和IRET
指令用於系統呼叫- 系統呼叫時,堆疊切換和特權級的轉換
- 函式呼叫
CALL
和RET
用於常規呼叫- 常規呼叫時沒有堆疊切換
三者開銷
開銷大於函式呼叫
中斷、異常和系統呼叫開銷
- 引導機制
- 建立核心堆疊
- 驗證引數
- 核心態對映到使用者態的地址空間
- 更新頁面對映許可權
- 核心態獨立地址空間
TLB
系統呼叫示例
ucore
中庫函式read()
的功能是讀檔案
user/libs/file.h: int read(int fd, void *buf, int length)
庫函式read()
的引數和返回值
int fd
—檔案控制程式碼void *buf
—資料緩衝區指標int length
—資料緩衝區長度int return_value
: 返回讀出資料長度
庫函式read()
使用示例
in sfs_filtest1.c: ret = read(fd, data, len)
read函式實現
-
kern/trap/tranpentry.S: alltraps()
獲取中斷資訊組成的資料結構 -
kern/trap/trap.c: trap()
tf->trapno == T_SYSCALL
(系統呼叫對應的中斷向量) -
kern/syscall/syscall.c: syscall()
tf->tf_regs.reg_eax == SYS.read
(系統呼叫編號) -
kern/syscal/syscall.c: sys_read()
從
tf->sp
獲取fd, buf, length
-
kern/fs/sysfile.c: sysfile_read()
讀取檔案
-
kern/trap/trapentry.S: trapret()
IRET
3. 記憶體管理
3.1 計算機體系結構/記憶體層次
計算機體系結構
記憶體層次
作業系統的記憶體管理方式
-
抽象
-
保護
-
共享
-
虛擬化
記憶體管理方式
- 重定位(relocation)一個程式佔一個連續的地址空間(太大不好放置)
- 分段(segmentation)將程式分段,段內地址連續
- 分頁(paging)
- 虛擬儲存(virtual memory)
- 目前多數系統如linux 採用按需頁式虛擬儲存
實現高度依賴硬體
- 與計算機儲存架構耦合
- MMU(記憶體管理單元):處理CPU儲存訪問請求的硬體
3.2 地址空間 & 地址生成
地址空間定義
實體地址空間——硬體支援的地址空間
- 起始地址0,直到\(MAX_{sys}\)
邏輯地址空間——在CPU執行的程式看到的地址
- 起始地址0,直到\(MAX_{prog}\)
邏輯地址生成
編譯->彙編->連結->程式載入(重定位)
地址生成時機和限制
- 編譯時
- 假設起始地址已知
- 如果起始地址改變,必須重新編譯
- 載入時
- 編譯時起始位置未知,編譯器需生成可重定位的程式碼(relocatable code)
- 載入時,生成絕對地址
- 執行時
- 執行時程式碼可移動
- 虛地址轉換(對映)硬體支援
地址生成過程
- CPU
- ALU:需要邏輯地址的內容
- MMU:進行邏輯地址和實體地址的轉換
- CPU控制邏輯:給匯流排傳送實體地址請求
- 記憶體
- 傳送實體地址的內容給CPU
- 或接收CPU資料到實體地址
- 作業系統
- 建立邏輯地址LA和實體地址PA的對映
地址檢查
3.3 連續記憶體分配
給程式分配一塊不小於指定大小的連續的實體記憶體區域(重定位)
記憶體碎片
空閒記憶體不能被利用
外部碎片:分配單元之間的未被使用記憶體
內部碎片:分配單元內部的未被使用記憶體,取決於分配單元大小是否要取整
動態分配
動態分割槽分配
- 當程式被載入執行時,分配一個程式指定大小可變的分割槽(塊,記憶體塊)
- 分割槽的地址是連續的
作業系統需要維護的資料結構
- 所有程式的已分配分割槽
- 空閒分割槽(empty-blocks)
動態分割槽分配策略
最先匹配
找到第一個滿足的就行
原理&實現
- 空閒分割槽列表按照地址順序排序
- 分配過程中,搜尋一個合適的分割槽(大於指定大小)
- 釋放分割槽時,檢查是否可與臨近的空閒分割槽合併
優點
- 簡單
- 在高地址空間有大塊的空閒分割槽
缺點
- 外部碎片
- 分配大塊時較慢
最佳匹配
遍歷一個最佳的
原理&實現
- 空閒分割槽列表按照大小排序
- 分配時,查詢一個合適的分割槽
- 釋放時,查詢並且合併臨近的空閒分割槽(如果找到)
優點
- 大部分分配的尺寸較小時,效果很好
- 可避免大的空閒分割槽被拆分
- 可減小外部碎片的大小
- 相對簡單
缺點
- 外部碎片
- 釋放分割槽較慢
- 容易產生很多無用的小碎片
最差匹配
找到最大空閒空間
原理&實現
- 空閒分割槽列表按由大到小排序
- 分配時,選最大的分割槽
- 釋放時,檢查是否可與臨近的空閒分割槽合併,進行合併,並調整空閒分割槽列表順序
優點
- 中等大小的分配較多時,效果最好
- 避免出現太多的小碎片
缺點
- 釋放分割槽較慢
- 外部碎片
- 容易破壞大的空閒分割槽,因此難以分配大的分割槽
碎片整理
通過調整程式佔用的分割槽位置來減少或避免分割槽碎片
碎片緊湊(compaction)
- 通過移動分配給程式的記憶體分割槽,以合併外部碎片
- 碎片緊湊的條件
- 所有的應用程式可動態重定位
- 解決的問題
- 執行時不能搬動
- 開銷
分割槽對換(swap in/out)
通過搶佔並回收處於等待狀態程式的分割槽,以增大可用記憶體空間
需要解決的問題?
- 交換哪個程式
開銷大
3.4 夥伴系統
buddy system
整個可分配的分割槽大小為 \(2^u\),需要的分割槽大小\(s\)
- 若 \(2^{u-1} < s \leq 2^u\),把整個塊分配給該程式
- 若 \(s \leq 2^{u-1}\),將當前空閒分割槽等分為兩個大小相同的空閒分割槽,重複劃分,直到大於0.5倍區域
夥伴系統的實現
資料結構
- 空閒塊按大小和起始地址組織成二維陣列
- 初始狀態:只有一個空閒塊
分配過程
- 由小到大在空閒塊陣列中找最小的可用空閒塊
- 如空閒塊過大,對可用空閒塊進行二等分,直到得到合適的可用空閒塊
釋放過程
- 把釋放的塊放入空閒塊陣列
- 合併滿足合併條件的空閒塊
合併條件
- 大小相同\(2^i\)
- 地址相鄰
- 起始地址較小的塊的起始地址必須是\(2^{i+1}\)的倍數
用於做核心的分配
例子
3.5 非連續記憶體分配
連續記憶體分配缺點
- 分配給程式的實體記憶體必須連續
- 存在外碎片和內碎片
- 記憶體分配的動態修改困難
- 記憶體利用率低
設計目標
提高記憶體利用效率和管理靈活性
- 允許一個程式使用非連續的實體地址空間
- 允許共享程式碼與資料
- 支援動態載入和動態連結
實現
需要解決的問題
- 如何實現虛擬地址和實體地址的轉換
- 軟體實現(靈活,開銷大)
- 硬體實現(夠用,開銷小)
- 非連續分配的硬體輔助機制
- 如何選擇非連續分配中的記憶體分塊大小
- 段式儲存管理(segmentation)
- 頁式儲存管理(paging)
- 如何選擇非連續分配中的記憶體分塊大小
3.5 段式儲存管理
段地址空間
程式的段地址空間由多個段組成
- 主程式碼段
- 子模組程式碼段
- 公用庫程式碼段
- 堆疊段(stack)
- 堆資料(heap)
- 初始化資料段
- 符號表等
段式儲存的目的:更細粒度和靈活的分離與共享
段內需要連續,段之間不連續
段訪問機制
段的概念
- 段表示訪問方式和儲存資料等屬性相同的一段地址空間
- 對應一個連續的記憶體“塊”
- 若干個段組成程式邏輯地址空間
段訪問
邏輯地址由二元組(s, addr)表示
- s: 段號
- addr: 段內偏移
段訪問的硬體實現
3.6 頁式儲存管理
概念
頁幀(幀、物理頁面,Frame,Page Frame)
- 把實體地址空間劃分為大小相同的基本分配單位
- 2的n次方,如512,4096,8192
- 實體記憶體被劃分成大小相等的幀
- 記憶體實體地址的表示:二元組\((f, o)\)
- \(f\)——幀號(\(F\)位,共有\(2^F\)個幀)
- \(o\)——幀內偏移(\(S\)位,每幀有\(2^S位元組\))
- 實體地址 \(= f * 2^S + o\)
計算例項
頁面(頁、邏輯頁面,Page)
-
程式邏輯地址空間被劃分為大小相等的頁
-
頁內偏移 = 幀內偏移
-
通常:頁號大小不等於幀號大小
-
程式邏輯地址的表示:二元組 \((p, o)\)
- \(p\)——頁號(\(P\)位,共有\(2^P\)個幀)
- \(o\)——頁內偏移(\(S\)位,每幀有\(2^s位元組\))
- 虛擬地址 \(= p * 2^S + o\)
頁面到頁幀
- 邏輯地址到實體地址
- 頁表
- MMU/TLB
地址轉換
- 頁到幀的對映
- 邏輯地址中的頁號是連續的
- 實體地址中的幀號是不連續的
- 不是所有的頁都有對應的幀
轉換過程
3.7 頁表
頁表結構
-
每個程式都有一個頁表
- 每個頁面對應一個頁表項
- 隨程式執行狀態而動態變化
- 頁表基址暫存器(PTBR:Page Table Base Register)(儲存基地址位置)
- 頁表項組成:
- 幀號: f
- 頁表項標誌:
- 存在位 resident bit
- 修改位 dirty bit
- 引用位 clock / reference bit
頁錶轉換例項
效能問題
- 記憶體訪問效能問題
- 訪問一個記憶體單元需要2次訪問
- 第一次訪問:獲取頁表項
- 第二次訪問:訪問資料
- 訪問一個記憶體單元需要2次訪問
- 頁表大小問題
- 頁表可能非常大
- 64位機器如果每頁1024位元組,那麼一個頁表的大小會是多少?
- 如何處理?
- 快取(Caching)(減少訪存次數,快表)
- 間接(Indirection)訪問(多級頁表)
快表TLB
translation look-aside Buffer
快取近期訪問的頁表項
-
TLB使用關聯儲存(associateive memory)實現,具備快速訪問效能
-
如果TLB命中,物理頁號可用很好被獲取
-
未命中,對應的表項被更新到TLB中
多級頁表
通過間接引用將頁號分成k級
- 建立頁表“樹”
- 減少每級頁表的長度
- 通過存在位,若不存在,則不構建下一級的頁表
例項
反置頁表
減少頁表儲存空間
大地址空間(64-bits)系統,多級頁表變得繁瑣
- 比如:5級頁表
- 邏輯(虛擬)地址空間增長速度快於實體地址空間
頁暫存器和反置頁面的思路
- 不讓頁表與邏輯地址空間的大小相對應
- 讓頁表與實體地址空間的大小相對應
頁暫存器
page registers
物理幀直接與頁暫存器關聯
每個物理幀與一個頁暫存器(page register)關聯,暫存器內容包括:
- 使用位:此幀是否被程式佔用
- 佔用頁號:對應的頁號p
- 保護位: 標記可讀可寫
頁暫存器示例
- 實體記憶體大小:4096*4096 = 4 K * 4 KB = 16 MB
- 頁面大小:4096 bytes = 4 KB
- 頁幀數:4096 = 4 K
- 頁暫存器使用的空間(假設每個頁暫存器佔8位元組):
- 8 * 4096 = 32 KB
- 頁暫存器帶來的開銷:
- 32 K / 16 M = 0.2%
- 虛擬記憶體的大小:任意
優點
- 頁表大小相對於實體記憶體而言很小
- 頁表大小與邏輯地址空間大小無關
缺點
- 頁表資訊對調後,需要依據幀號可找頁號
- 在頁暫存器中搜尋邏輯地址的頁號
頁暫存器中的地址轉換
-
對邏輯地址進行Hash對映,以減小搜尋範圍
-
需要解決可能的衝突
用快表快取頁表項後的頁暫存器搜尋步驟
- 對邏輯地址進行Hash變換
- 在快表中查詢對應頁表項
- 有衝突時遍歷衝突項鍊表
- 查詢失敗時,產生異常
快表的限制
- 快表的容量限制
- 功耗限制(StrongARM上快表功耗佔27%)
反置頁表
所有的程式共同使用一張頁表,這張頁表中的條目的數量和記憶體中物理的頁框的數量是一樣的。
反置頁表中每個條目擁有以下欄位:
- 頁號
- 程式ID
- 控制位
- 連結指標---如果出現程式共享記憶體的情況,就會用到連結指標
基於Hash對映值查詢對應頁表項中的幀號
- 程式標識與頁號的Hash值可能有衝突
- 頁表項中包括保護位、修改位、訪問位和存在位等表示
3.8 段頁式儲存管理
段式儲存在記憶體保護方面有優勢,頁式儲存在記憶體利用和優化轉移到儲存方面有優勢
在段式儲存管理基礎上,給每個段加一級頁表
記憶體共享
通過指向相同的頁表基地址,實現程式間的段共享
4. 虛擬儲存
4.1 需求背景
程式規模的增長速度遠遠大於儲存器容量的增長速度
理想的儲存器
- 容量大,速度快,價格低的非易失性儲存器
實際儲存器
-
儲存器層次結構
儲存抽象
計算機系統時常出現記憶體空間不夠用
-
覆蓋
應用程式手動把需要的指令和資料儲存在記憶體中
-
交換
作業系統自動把暫時不能執行的程式儲存到外存中
-
虛擬儲存
在有限容量的記憶體中,以頁為單位自動裝入更多更大程式
虛擬儲存技術的目標
只把部分程式放到記憶體中,從而執行比實體記憶體大的程式
- 由作業系統自動完成
實現程式在記憶體與外存之間的交換,從而獲取更多的空閒記憶體空間
- 在記憶體和外存之間只交換程式的部分內容
4.2 覆蓋技術
在較小的可用記憶體中執行較大的程式
實現方法
依據程式邏輯結構,將程式劃分為若干功能相對獨立的模組,將不會同時執行的模組共享同一塊記憶體區域。
- 必要部分的程式碼和資料常駐
- 可選部分放在其他程式模組中,在需要的時候裝載
- 不存在呼叫關係的模組,可相互覆蓋,共同用同一塊記憶體區域
示例
不足
-
增加程式設計困難
-
增加執行時間
4.3 交換技術
增加正在執行或需要執行的程式的記憶體
實現方法
-
可將暫時不能執行的程式放到外存
-
換入換出的基本單位
- 整個程式的地址空間
-
換出
- 把一個程式的整個地址空間儲存到外存
-
換入
- 將外存中某程式的地址空間讀入到記憶體
問題
- 何時交換?
- 只當記憶體空間不夠或有不夠的可能時換出
- 交換區大小
- 存放所有使用者程式的所有記憶體映像的拷貝
- 換入之後的重定位(地址位置可能發生了改變)
- 採用動態地址對映的方法
覆蓋與交換的比較
覆蓋
- 只能發生在沒有呼叫關係的模組間
- 程式設計師需要給出模組間的邏輯覆蓋結構
- 發生在執行程式的內部模組間
交換
- 以程式位單位
- 不需要模組間的邏輯覆蓋結構
- 發生在記憶體程式間
4.4 區域性性原理
principle of locality
程式在執行過程中的一個較短時期,所執行的指令地址和指令的運算元地址,分別侷限於一定區域
-
時間區域性性
一條指令或者資料的一次執行和下次執行(訪問)都集中在一個較短的時期內
-
空間區域性性
當前指令和臨近的幾條指令,都集中在一個較小的區域
-
分支區域性性
一條跳轉指令的兩次執行,很可能跳到相同的記憶體位置
4.5 虛擬儲存概念
將不常用的部分記憶體塊暫存到外存
原理
-
裝載程式時
只將當前指令執行需要的部分頁面或段裝入記憶體
-
指令執行中需要的指令和資料不在記憶體中稱為缺頁或缺段
處理器通知作業系統將相應的頁面或段調入記憶體
-
作業系統將記憶體中暫時不用的頁面或段儲存到外存中去
實現方式
- 虛擬頁式儲存
- 虛擬段式儲存
基本特徵
- 不連續性
- 實體記憶體分配非連續
- 虛擬地址空間使用非連續
- 大使用者空間
- 提供給使用者的虛擬記憶體大於實際的實體記憶體
- 部分交換
- 虛擬儲存只對部分虛擬地址空間進行調入和調出
支援技術
- 硬體
- 頁式或短時儲存中的地址轉換機制
- 作業系統
- 管理記憶體和外存間頁面或段的換入和換出
4.6 虛擬頁式儲存
在頁式儲存管理的基礎上,增加請求調頁和頁面置換
思路
- 當使用者程式裝載到記憶體執行時,只裝入部分頁面,就啟動程式
- 程式在執行中發現有需要的程式碼或資料不在記憶體時,則向系統發出缺頁異常請求
- 作業系統在處理缺頁異常時,將外存中相應的頁面調入記憶體,使得程式能繼續進行
虛擬頁式儲存中的地址轉換
頁表項結構
- 駐留位:表示該頁是否在記憶體
- 1:在記憶體,頁表項有效,可用
- 0:在外存,訪問該頁表項將導致缺頁異常
- 修改位:表示在記憶體中的該頁是否被修改過
- 回收該物理頁面時。據此判斷是否要把它的內容寫回
- 訪問位:表示該頁面是否被訪問過(讀或寫)
- 用於頁面置換演算法
- 保護位:表示該頁的訪問方式
- 可讀可寫等
虛擬頁式儲存示例
X86頁表結構
4.7 缺頁異常
處理流程
外存管理
- 何處儲存未被對映的頁?
- 方便找到外村中的頁面內容
- 交換空間(磁碟或者檔案)
- 採用特殊格式儲存未被對映的頁面
- 虛擬頁式儲存中的外存選擇
- 程式碼段:可執行二進位制檔案
- 動態載入的共享庫程式段:動態呼叫的庫檔案
- 其他段:交換空間
虛擬頁式儲存管理的效能
-
有效儲存訪問時間(effective memory access time EAT)
EAT = 訪存時間*(1-p)+缺頁異常處理時間 *缺頁率 p
5. 置換演算法
5.1 概念
當出現缺頁異常,需調入新頁面而記憶體已滿時,置換演算法選擇被置換的物理頁面
設計目標
-
儘可能減少頁面的調入調出次數
-
把未來不再訪問或短期內不訪問的頁面調出
頁面鎖定(frame locking)
- 描述必須常駐記憶體的邏輯頁面
- 作業系統的關鍵部分
- 要求響應速度的程式碼和資料
- 頁表中的鎖定標註位 lock bit
評價方法
- 記錄程式訪問記憶體的頁面軌跡
- 評價方法
- 模擬頁面置換行為,記錄產生缺頁的次數
- 更少的缺頁,更好的效能
分類
- 區域性頁面置換演算法
- 置換頁面的選擇範圍僅限於當前程式佔用的物理頁面內
- 最優演算法、先進先出演算法、最近最久未使用演算法
- 時鐘演算法、最不常用演算法
- 全域性頁面置換演算法
- 置換頁面的選擇範圍是所有可換出的物理頁面
- 工作集演算法、缺頁率演算法
5.2 區域性頁面置換演算法
最優置換演算法
置換在未來最長時間不訪問頁面
演算法實現
- 缺頁時,計算記憶體中每個邏輯頁面的下次訪問時間
- 選擇未來最長時間不訪問的頁面
演算法特徵
- 缺頁最少,是理想情況
- 無法預知訪問時間,無法實現
- 作為置換演算法的效能評價依據
演算法示例
先進先出演算法
First-In, First-out, FIFO
演算法實現
- 維護一個記錄所有位於記憶體中的邏輯頁面連結串列
- 連結串列元素按駐留記憶體的時間排序,鏈首最長,鏈尾最短
- 出現缺頁是,選擇鏈首進行置換,新頁面加到鏈尾
特徵
- 實現簡單
- 效能較差,調出的頁面可能是經常訪問的
- 程式分配物理頁面數增加時,缺頁並不一定減少(Belady現象)
- 很少單獨使用
演算法示例
最近最久未使用演算法
Least Recently Used, LRU
演算法思路
- 選擇最長時間沒有被引用的頁面進行置換
- 如某些頁面長時間未被訪問,則再將來還可能會長時間不會被訪問
演算法實現
- 缺頁時,計算記憶體中每個邏輯頁面的上次訪問時間
- 選擇上一次使用到當前時間最長的頁面
特徵
- 最優置換演算法的近似
演算法示例
LRU演算法的可能實現方法
頁面連結串列
- 系統維護一個按最近一次訪問時間排序的頁面連結串列
- 連結串列首節點是最近使用的頁面
- 連結串列尾節點是最久未使用的頁面
- 訪問記憶體時,找到相應的頁面,並把它移到連結串列之首
- 缺頁時,置換連結串列尾節點的頁面
活動頁面棧
- 訪問頁面時,將此號壓入棧頂,並將棧內相同的頁號抽出
- 缺頁時,置換棧底的頁面
特徵
- 開銷大
演算法示例
時鐘置換演算法
Clock
僅對頁面的訪問情況進行大致統計
資料結構
- 在頁表項增加訪問位
- 頁面組織成環形連結串列
- 指標指向最先調入的頁面
演算法
- 訪問頁面時,在頁表項記錄頁面訪問情況
- 缺頁時,從指標處開始順序查詢未被訪問的頁面進行置換
特徵
演算法是LRU和FIFO的折中
演算法實現
- 頁面裝如記憶體時,訪問位初始化位0
- 訪問頁面時,訪問位置1
- 缺頁時,從指標當前位置順序檢查環形連結串列
- 訪問位為0,則置換該頁
- 訪問位為1,則訪問位置0,並指標移動到下一頁面,直到找到可置換的頁面
演算法示例
改進的Clock演算法
減少修改頁的缺頁處理開銷
演算法
- 在頁面中增加修改位,並在訪問時進行相應修改
- 缺頁時,修改頁面標誌位,以跳過有修改的頁面
演算法示例
最不常用演算法
LFU Least Frequently Used
缺頁時,置換訪問次數最少的頁面
演算法實現
- 每個頁面設定一個訪問計數
- 訪問頁面時,訪問計算+1
- 缺頁時,置換計算最小的頁面
特徵
- 演算法開銷大
- 開始時頻繁使用,但以後不使用的頁面很難置換
- 解決方法:計數定期右移
LRU和LFU區別
- LRU關注多久未訪問,時間越短越好
- LFU關注訪問次數,次數越多越好
演算法示例
Belady現象
採用FIFO等演算法時,可能出現分配的物理頁面數增加,缺頁次數反而升高的異常現象
原因
- FIFO演算法的置換特徵與程式訪問記憶體的動態特徵矛盾
- 被它置換出去的頁面並不一定是程式近期不會訪問的
思考
哪些演算法沒有此現象?
FIFO有,LRU沒有此現象
FIFO演算法示例
三個頁,缺頁次數9
四個頁,缺頁次數10
LRU、FIFO和Clock的比較
LRU演算法和FIFO本質上都是先進先出的思路
- LRU依據頁面的最近訪問時間排序
- LRU需要動態調整順序
- FIFO依據頁面進入記憶體的時間排序
- FIFO的頁面進入時間是固定不變的
LRU可退化成FIFO
- 所有頁面進入記憶體後沒有再次訪問,最近訪問和進入時間相同
LRU演算法效能好,但系統開銷較大
FIFO演算法系統開銷較小,會發生Belady現象
Clock演算法是它們的折中
- 頁面訪問時,不動態調整頁面在連結串列中的順序,僅做標記
- 缺頁時,再把它移動到連結串列末尾
對於未被訪問的頁面,Clock和LRU演算法的表現以一樣好
對於被訪問過的頁面,Clock演算法不能記錄準確訪問順序,而LRU可以
5.3 全域性頁面置換演算法
工作集置換演算法
給程式分配可變數目的物理頁面
需要解決的問題
- 程式在不同階段的記憶體需求是變化的
- 分配給程式的記憶體也需要在不同的階段有所變化
- 全域性置換演算法需要確定分配給程式的物理頁面數
CPU利用率與併發程式數的關係
CPU利用率與併發程式存在相互促進與制約的關係
- 程式數少時,提高併發程式數,可提高CPU利用率
- 併發程式導致記憶體訪問增加
- 併發程式的記憶體訪問會降低了訪存的區域性性特徵
- 區域性特徵的下降會導致缺頁率上升和CPU利用率下降
工作集
一個程式當前正在使用的邏輯頁面集合,可表示位二元函式\(W(t, \Delta)\)
- \(t\)當前時刻
- \(\Delta\)稱為工作集視窗,即一個定長的頁面訪問時間視窗
- \(W(t, \Delta)\)是指在當前時刻 $t $ 前的 $\Delta $ 時間視窗中所有訪問頁面所組成的集合
- \(|W(t, \Delta)|\)指工作集的大小,即頁面數目
工作集的變化
-
程式開始執行後,隨著訪問新頁面逐步建立較穩定的工作集
-
當記憶體訪問的區域性性區域的位置大致穩定時,工作集的大小也大致穩定
-
區域性性區域的位置改變時,工作集快速擴張和收縮過渡到下一個穩定值
常駐集
在當前時刻,程式實際駐留在記憶體當中的頁面集合
工作集與常駐集的關係
- 工作集是程式在執行過程中固有的性質
- 常駐集取決於系統分配給程式的物理頁面數目和頁面置換演算法
缺頁率與常駐集的關係
- 常駐集包含工作集時,缺頁率小
- 工作集發生劇烈變動時,缺頁多
- 程式常駐集大小達到一定數目後,缺頁率也不會明顯下降
思路
- 換出不在工作集中的頁面
視窗大小x
- 當前時刻前x個記憶體訪問的頁引用是工作集,x被稱為視窗的大小
實現方法
- 訪存連結串列:維護視窗內的訪存頁面連結串列
- 訪存時,換出不在工作集的頁面;更新訪存連結串列
- 缺頁時,換入頁面;更新訪存連結串列
演算法示例
缺頁率置換演算法
PFF,Page-Fault-Frequency
缺頁率(page fault rate)
缺頁次數/記憶體訪問次數 或 缺頁平均時間間隔的倒數
- 影響缺頁率的因素
- 頁面置換演算法
- 分配給程式的物理頁面數目
- 頁面大小
- 程式的編寫方法
通過調節常駐集大小,使每個程式的缺頁率保持一個合理的範圍內
- 若程式缺頁率過高,則增加常駐集以分配更多的物理頁面
- 若程式缺頁率過低,則減少常駐集以減少物理頁面
演算法實現
- 訪存時,設定引用標誌位
- 缺頁時,計算從上次缺頁時間到現在時間的間隔
- 如果時間間隔大於給定時間T,則置換所有在間隔時間內沒有被引用的頁
- 如果時間間隔小於等於給定時間T,則增加缺失頁到常駐集
缺頁率置換演算法示例
視窗大小為2
抖動和負載控制
抖動問題(thrashing)
- 程式物理頁面太少,不能包含工作集
- 造成大量缺頁,頻繁置換
- 程式執行速度變慢
產生抖動的原因
- 隨著駐留記憶體的程式數目增加,分配給每個程式的物理頁面數不斷減少,缺頁率上升
作業系統需在併發水平和缺頁率之間達到一個平衡
- 選擇一個適當的程式數目和程式需要的物理頁面數
負載控制
通過調節併發程式數(MPL)來進行系統負載控制
- \(\sum WSi\) = 記憶體的大小
- 平均缺頁時間間隔(MTBF)= 缺頁異常處理時間(PFST)
6. 程式
6.1 程式的概念
程式是指一個具有一定獨立功能的程式在一個資料集合上的一次動態執行過程
程式組成
程式包含了正在執行的一個程式的所有狀態資訊
- 程式碼
- 資料
- 狀態暫存器
- 通用暫存器
- 程式佔用系統資源
程式特點
- 動態性
- 併發性
- 程式可以被獨立排程並佔用處理機執行
- 獨立性
- 不同的程式不會互相影響
- 制約性
- 因訪問共享資料、資源或程式間同步而產生的制約
程式與程式的聯絡
- 程式是作業系統處於執行狀態程式的抽象
- 程式 = 檔案(靜態可執行檔案)
- 程式 = 執行中的程式 = 程式 + 執行狀態
- 同一個程式的多次執行過程對應為不同程式
- 如命令 ls 的多次執行對應多個不同程式
- 程式執行需要的資源
- 記憶體:儲存程式碼和資料
- CPU:執行指令
程式與程式的區別
- 程式是動態的,程式是靜態的
- 程式是有序程式碼的集合
- 程式是程式的執行,程式有核心態、使用者態
- 程式是暫時的,程式是永久的
- 程式是一個狀態變化的過程
- 程式可長久儲存
- 程式與程式的組成不同
- 程式的組成包括程式、資料和程式控制塊
6.2 程式控制塊
PCB,Process Control Block
作業系統管理控制程式執行所用的資訊集合
- 作業系統由PCB來描述程式的基本情況以及執行變化的過程
- PCB是程式存在的唯一標誌
- 每個程式都在作業系統中有一個對應的PCB
使用流程
- 程式建立
- 生成該程式的PCB
- 程式終止
- 回收它的PCB
- 程式的組織管理
- 通過對PCB的組織管理來實現
控制塊內容
- 程式標識資訊
- 處理機現場保護
- 程式控制資訊
程式控制資訊
- 排程資訊和狀態資訊
- 程式和處理機使用情況排程
- 程式間通訊資訊
- 程式間通訊相關的各種標識
- 儲存管理資訊
- 指向程式映像儲存空間資料結構
- 程式所佔資源
- 程式使用的系統資源,如開啟檔案等
- 有關資料結構連線資訊
- 與PCB相關的程式佇列
程式控制塊的組織
連結串列
同一狀態的程式其PCB成一連結串列,多個狀態對應多個不同的連結串列
- 各狀態的程式形成不同的連結串列:就緒連結串列、阻塞連結串列
索引表
同一狀態的程式歸入一個索引表(由索引指向PCB)
多個狀態對應多個不同的索引表
- 各狀態的進行形成不同的索引表:就緒索引表、阻塞索引表
6.3 程式狀態
程式的生命週期劃分
- 程式建立
- 程式執行
- 程式等待
- 程式搶佔
- 程式喚醒
- 程式結束
程式切換
6.4 三狀態程式模型
6.5 掛起程式模型
處於掛起狀態的程式映像在磁碟上,目的是減少程式佔用記憶體
- 等待掛起狀態(Blocked-suspend)
- 程式在外存並等待某事件的出現
- 就緒掛起狀態(Ready-suspend)
- 程式在外存,但只要進入記憶體,即可執行
- 掛起(Suspend):把一個程式從記憶體轉到外存
- 等待到等待掛起
- 沒有程式處於就緒狀態或就緒程式要求更多記憶體資源
- 就緒到就緒掛起
- 當由高優先順序等待(系統認為會很快就緒的)程式和低優先順序就緒程式
- 執行到就緒掛起
- 對搶先式分時系統,當有高優先順序等待掛起程式因事件出現而進入就緒掛起
- 等待到等待掛起
在外存時的狀態轉換
-
等待掛起到就緒掛起
- 當有等待掛起程式因相關事件出現
-
啟用(Activate):把一個程式從外存轉到記憶體
-
就緒掛起到就緒
沒有就緒程式或掛起就緒程式優先順序高於就緒程式
-
等待掛起到等待
當一個程式釋放足夠記憶體,並有高優先順序等待掛起程式
-
狀態佇列
- 由作業系統來維護一組佇列,表示系統中所有程式的當前狀態
- 不同佇列表示不同狀態
- 就緒佇列、各種等待佇列
- 根據程式狀態不同,程式PCB加入相應佇列
7. 執行緒
7.1 為什麼需要執行緒?
單程式播放
多程式實現
解決思路
在程式內部增加一類實體,滿足以下特性:
- 實體之間可以併發執行
- 實體之間共享相同的地址空間
這種實體就是執行緒(Thread)
7.2 執行緒的概念
執行緒是程式的一部分,描述指令流執行狀態。它是程式中的指令執行流的最小單元,是CPU排程的基本單位。
程式的資源分配角色:程式由一組相關資源構成,包括地址空間(程式碼段、資料段)、開啟的檔案等各種資源
執行緒的處理機排程角色:執行緒描述在程式資源環境中的指令流執行狀態
程式和執行緒的關係
執行緒 = 程式 - 共享資源
執行緒的優點
- 一個程式中可以同時存在多個執行緒
- 各個執行緒之間可以併發執行
- 各個執行緒之間可以共享地址空間和檔案資源
執行緒的缺點
- 一個執行緒崩潰,會導致其所屬程式的所有執行緒崩潰
不同作業系統對執行緒的支援
路由器就是單程式多執行緒系統
程式與執行緒的比較
- 程式是資源分配的單位,執行緒是CPU排程單位
- 程式擁有一個完整的資源平臺,而執行緒只獨享指令流執行的必要資源,如暫存器和棧
- 執行緒具有就緒、等待和執行三種基本狀態和狀態間的轉換關係
- 執行緒能減少併發執行的時間和空間開銷
- 執行緒的建立時間比程式短
- 執行緒的終止時間比程式短
- 同一程式內的執行緒切換時間比程式短
- 由於同一程式的各執行緒間共享記憶體和檔案資源,可不通過核心進行直接通訊
7.3 使用者執行緒
執行緒的三種實現方式
-
使用者執行緒:在使用者空間實現
POSIX Pthreads, Mach C-threads, Solaris threads
-
核心執行緒:在核心中實現
Window,Solaris, Linux
-
輕量級程式:在核心中實現,支援使用者執行緒
Solaris (LightWeight Process)
概念
由一組使用者級的執行緒庫函式來完成執行緒的管理,包括執行緒的建立、終止、同步和排程等。
- 不依賴與作業系統的核心
- 核心不瞭解使用者執行緒的存在
- 可用於不支援執行緒的多程式作業系統
- 在使用者空間實現的執行緒機制
- 每個程式由私有的執行緒控制塊(TCB)列表
- TCB由執行緒庫函式維護
- 同一程式內的使用者執行緒切換速度快
- 無需核心態和使用者態切換
- 允許每個程式擁有自己的執行緒排程演算法
不足
- 執行緒發起系統呼叫而阻塞時,則整個系統進入等待
- 不支援基於執行緒的處理機搶佔
- 除非當前執行執行緒主動放棄,他所在程式的其他執行緒無法搶佔CPU
- 只能按程式分配CPU時間
- 多個執行緒程式中,每個執行緒時間會變少
7.4 核心執行緒
程式由核心通過系統呼叫實現的執行緒機制,由核心完成執行緒的建立、終止和管理。
特徵
- 由核心維護PCB和TCB
- 執行緒執行系統呼叫而被阻塞不影響其他執行緒
- 執行緒的建立、終止和切換相對較大
- 通過系統呼叫/核心函式,在核心實現
- 以執行緒為單位進行CPU時間分配
- 多執行緒的程式可獲得更多CPU時間
7.5 輕權程式(LightWeight Process)
核心支援的使用者執行緒。一個程式可有一個或多個輕量級程式,每個輕權程式由一個單獨的核心執行緒來支援。
使用者執行緒和核心執行緒的對應關係
8. 程式控制
8.1 程式切換
(程式切換)上下文切換
- 暫停當前執行程式,從執行狀態變成其他狀態
- 排程另一個程式從就緒狀態變成執行狀態
程式切換的要求
- 切換前,儲存程式上下文
- 切換後,恢復程式上下文
- 快速切換
程式生命週期的資訊
- 暫存器(PC,SP, )
- CPU狀態
- 記憶體地址空間
程式控制塊PCB:核心的程式狀態記錄
- 核心為每個程式維護了對應的程式控制塊(PCB)
- 核心將相同狀態的程式的PCB放置在同一佇列
8.2 程式建立
Windows程式建立API:CreateProcess(filename)
-
建立時關閉所有在子程式裡的檔案描述符
CreateProcess(filename,CLOSE_FD)
-
建立時改變子程式的環境
CreateProcess(filename,CLOSE_FD,new_envp)
-
等等
Unix程式建立系統呼叫:fork/exec
-
fork()把一個程式複製成二個程式
parent(old PID),child(new PID)
-
exec()用新程式來重寫當前程式
PID沒有該變
fork的地址空間複製
fork使用示例
程式碼
8.3 程式載入
程式載入和執行系統呼叫exec()
- 允許程式,“載入”一個完全不同的程式,並從main開始執行(即_start)
- 允許程式載入時指定啟動引數(argc, argv)
- exec呼叫成功時
- 是相同的程式
- 執行了不同的程式
- 程式碼段、堆疊和堆(heap)等完全重寫
8.4 程式等待與退出
wait()系統呼叫用於父程式等待子程式的結束
- 子程式結束時通過exit()向父程式返回一個值
- 父程式通過wait()處理返回值
wait()系統呼叫的功能
-
有子程式存活時,父程式進入等待狀態,等待子程式的返回結果
當某子程式呼叫exit()時,喚醒父程式,將exit()返回值作為父程式中wait的返回值
-
有殭屍子程式等待時,wait()立即返回其中一個值
-
無子程式存活時,wait()立刻返回
程式的有序終止exit()
- 程式結束執行時呼叫exit,完成資源回收
- exit系統呼叫的功能
- 將呼叫引數作為程式的“結果”
- 關閉所有開啟的檔案等佔用資源
- 釋放記憶體
- 釋放大部分程式相關的核心資料結構
- 檢查是否父程式是存活著的
- 如存活,保留結果的值直到父程式需要它,進入殭屍(zombie/defunct)狀態
- 如果沒有,它釋放所有的資料結構,程式結束
- 清理所有等待的殭屍程式
- 程式終止是最終的垃圾收集(資源回收)
9. 處理機排程
9.1 處理機排程概念
CPU資源的分時多工
-
程式切換:CPU資源的當前佔用者切換
- 儲存當前程式在PCB中的執行上下文(CPU狀態)
- 恢復下一個程式的執行上下文
-
處理機排程
- 從就緒佇列中挑選下一個佔用CPU執行的程式
- 從多個可用CPU中挑選就緒程式可使用的CPU資源
-
排程程式:挑選就緒程式的核心函式
排程策略:依據什麼原則挑選程式/執行緒?
排程時機:什麼時候進行排程?
排程時機
- 核心執行排程程式的條件
- 程式從執行狀態切換到等待狀態
- 程式被終結了
- 非搶佔系統
- 當前程式主動放棄CPU時
- 可搶佔系統
- 中斷請求被服務例程響應完成時
- 當前程式被搶佔
- 程式時間片用完
- 程式從等待切換到就緒
9.2 排程準則
排程策略
確定如何從就緒佇列中選擇下一個執行程式
排程策略要解決的問題
- 挑選就緒佇列中的哪一個程式?
- 通過什麼樣的準則來挑選?
排程演算法
- 在排程程式中實現的排程策略
比較排程演算法的準則
- 哪一個策略/演算法較好?
- CPU使用率
- CPU處於忙狀態的時間百分比
- 吞吐量
- 單位時間內完成的程式數量
- 週轉時間
- 程式從初始化到結束的總時間
- 等待時間
- 程式在就緒佇列中的總時間
- 響應時間
- 從提交請求搭配產生響應所花費的總時間
- CPU使用率
處理機資源的使用模式
- 程式在CPU計算和I/O操作間交替
- 每次排程決定在下一個CPU計算時將哪個工作交給CPU
- 在時間片機制下,程式可能在結束當前CPU計算前被迫放棄CPU
吞吐量與延遲
-
排程演算法的要求
- 希望更快的服務
-
什麼是更快?
- 傳輸檔案的高頻寬、排程演算法的高吞吐量
- 玩遊戲的低延遲,排程演算法的低響應延遲
- 這兩個因素是獨立的
-
與水管的類比
- 低延遲:喝水的時候想要一開啟水龍頭就留出來
- 高頻寬:給游泳池充水希望從水龍頭裡同時流出大量的水,不介意是否存在延遲
處理機排程策略的響應時間目標
-
減少響應時間
及時處理使用者的輸入請求,儘快輸出反饋給使用者
-
減少平均響應時間的波動
在互動系統中,可預測性比高差異低平均更重要
-
低延遲排程改善了使用者的互動體驗
如果移動滑鼠時,螢幕中的游標沒動,使用者可能會重啟電腦
-
響應時間是作業系統的計算延遲
處理機排程策略的吞吐量目標
- 增加吞吐量
- 減少開銷(作業系統開銷,上下文切換)
- 系統資源的高效利用(CPU,I/O裝置)
- 減少等待時間
- 減少每個程式的等待時間
- 作業系統需要保證吞吐量不受使用者互動的影響
- 作業系統必須不時進行排程,即使存在許多互動任務
- 吞吐量是作業系統的計算頻寬
處理機排程策略的公平性目標
- 公平的定義
- 保證每個程式佔用相同的CPU時間
- 保證每個程式的等待時間相同
9.3 排程演算法
先來先服務演算法
FCFS:First Come First Served
依據程式進入就緒狀態的先後順序排列
- 程式進入等待或結束狀態時,就緒佇列中的下一個程式佔用CPU
FCFS演算法的週轉時間
先來先服務演算法的特徵
-
優點
- 簡單
-
缺點
-
平均等待時間波動較大
短程式可能排在長程式後面
-
I/O資源和CPU資源的利用率低
CPU密集型程式會導致I/O裝置閒置時,I/O密集型程式也等待
-
短程式優先演算法
SPN
選擇就緒佇列中執行時間最短程式佔用CPU進入執行狀態
- 就緒佇列按預期的執行時間來排序
短剩餘時間優先演算法(SRT):可執行搶佔CPU,只要執行時間更短
短程式優先演算法具有最優平均週轉時間
缺點
-
可能導致飢餓
連續的短程式流會使長程式無法獲得CPU資源
-
需要預知未來
- 如何預估下一個CPU計算的持續時間
- 簡單的解決方法:詢問使用者
- 使用者欺騙就殺死程式
- 使用者不知道
短程式優先演算法的執行時間預估
- 用歷史的執行時間來預估未來的執行時間
最高響應比優先演算法
HRRN
選擇就緒佇列中響應比R值最高的程式
- 基於短程式優先演算法基礎上改進
- 不可搶佔
- 關注程式的等待時間
- 防止無限制等待
時間片輪轉演算法
RR,Round-Robin
時間片
- 分配處理機資源的基本時間單元
演算法思路
- 時間片結束時,按FCFS演算法切換到下一個就緒程式
- 每隔(n-1)個時間片程式執行一個時間片q
演算法示例
時間片長度
- RR演算法開銷
- 額外的上下文切換
- 時間片太大
- 等待時間太長
- 極限情況退化成FCFS
- 時間片太小
- 反應快,但產生大量上下文切換
- 大量上下文切換開銷影響到系統吞吐量
- 時間片長度選擇目標
- 選擇一個合適的時間片長度
- 經驗規則:維持上下文切換開銷處於1%以內
多級佇列排程演算法
MQ
- 就緒佇列被劃分成多個獨立的子佇列
- 如:前臺(互動)、後臺(批處理)
- 每個佇列擁有自己的排程策略
- 如:前臺-RR、後臺-FCFS
- 佇列間的排程
- 固定優先順序
- 先處理前臺,然後處理後臺
- 可能導致飢餓
- 時間片輪轉
- 每個佇列都得到一個確定的能夠排程其程式的CPU總時間
- 如:80%CPU時間用於前臺,20%CPU時間用於後臺
- 固定優先順序
多級反饋佇列演算法
MLFQ
- 程式可在不同佇列間移動的多級佇列演算法
- 時間片大小隨優先順序級別增加而增加
- 如程式在當前的時間片沒有完成,則降到下一個優先順序
- MLFQ演算法的特徵
- CPU密集型程式的優先順序下降很快
- I/O密集型程式停留在高優先順序
公平共享排程演算法
FSS,Fair Share Scheduling
FSS控制使用者對系統資源的訪問
- 一些使用者組比其他使用者組更重要
- 保證不重要的組無法壟斷資源
- 未使用的資源按比例分配
- 沒有達到資源使用率目標的組獲得更高的優先順序
總結
- 先來先服務演算法(FCFS)
- 不公平,平均等待時間較差
- 短程式優先演算法(SPN)
- 不公平,平均週轉時間最小
- 需要精確預測計算時間
- 可能導致飢餓
- 最高響應比演算法(HRRN)
- 基於SPN排程
- 不可搶佔
- 時間片輪轉演算法(RR)
- 公平,平均等待時間較差
- 互動性很好
- 多級反饋佇列演算法(MLFQ)
- 多種演算法的整合
- 公平共享排程演算法(FSS)
- 公平是第一要素
9.4 實時排程和多處理器排程
實時作業系統
實時作業系統的定義
正確性依賴於其時間和功能兩方面的作業系統
實時作業系統的效能指標
- 時間約束的及時性(deadlines)
- 速度和平均效能相對不重要
實時作業系統的特性
- 時間約束的可預測性
實時任務
- 任務(工作單元)
- 一次計算,一次檔案讀取,一次資訊傳遞等等
- 任務屬性
- 完成任務所需要的資源
- 定時引數
週期實時任務
一系列相似的任務
- 任務有規律地重複
- 週期 p = 任務請求時間間隔 (0 < p)
- 執行時間 e = 最大執行時間 (0 < e < p)
- 使用率 U = e / p
硬時限和軟時限
- 硬時限(hard deadline)
- 錯過任務時限會導致災難性或非常嚴重的後果
- 必須驗證,在最壞情況下能夠滿足時限
- 軟時限(soft deadline)
- 通常能滿足任務時限
- 有時不能滿足,則降低要求
- 盡力保證滿足任務時限
- 通常能滿足任務時限
可排程性
可排程表示一個實時作業系統能夠滿足任務時限要求
- 需要確定實時任務的執行順序
- 靜態優先順序排程
- 動態優先順序排程
速率單調排程
(RM,Rate Monotonic)
- 通過週期安排優先順序
- 週期越短優先順序越高
- 執行週期最短的任務
最早截止時間優先演算法
(EDF,Earilest Deadline First)
- 截止時間越早優先順序越高
- 執行截止時間最早的任務
多處理器排程
特徵
- 多個處理機組成的一個多處理機系統
- 多處理機間可負載共享
對稱多處理器(SMP,Symmetric multiprocessing)排程
- 截止時間越早優先順序越高每個處理器執行自己的排程程式
- 排程程式對共享資源的訪問要進行同步
對稱多處理器的程式分配
- 靜態程式分配
- 程式從開始到結束都被分配到一個固定的處理機上執行
- 每個處理機有自己的就緒佇列
- 排程開銷小
- 各處理機可能忙閒不均
- 動態程式分配
- 程式在執行種可分配到任意空閒處理機執行
- 所有處理機共享一個公共的就緒佇列
- 排程開銷大
- 各處理機的負載是均衡的
9.5 優先順序反置
priority inversion
-
作業系統中出現高優先順序程式長時間等待低優先順序程式所佔用資源的現場
-
基於優先順序的可搶佔排程演算法存在優先順序反置
例如:有優先順序為A、B和C三個任務,優先順序A>B>C,任務A,B處於掛起狀態,等待某一事件發生,任務C正在執行,此時任務C開始使用某一共享資源S。在使用中,任務A等待事件到來,任務A轉為就緒態,因為它比任務C優先順序高,所以立即執行。當任務A要使用共享資源S時,由於其正在被任務C使用,因此任務A被掛起,任務C開始執行。如果此時任務B等待事件到來,則任務B轉為就緒態。由於任務B優先順序比任務C高,因此任務B開始執行,直到其執行完畢,任務C才開始執行。直到任務C釋放共享資源S後,任務A才得以執行。在這種情況下,優先順序發生了翻轉,任務B先於任務A執行。
優先順序繼承
Priority Inheritance
佔用資源的低優先順序程式繼承申請資源的高優先順序程式的優先順序
- 只在佔有資源的低優先順序程式被阻塞時,才提高佔有資源程式的優先順序
優先順序天花板協議
priority ceiling protocol
佔有資源程式的優先順序和所有可能申請該資源的程式的最高優先順序相同
-
不管是否發生等待,都提升佔用資源程式的優先順序
-
優先順序高於系統中的所有被鎖定的資源的優先順序上限,任務執行臨界區時就不會被阻塞
10. 同步互斥
10.1 背景
獨立程式:不和其他程式共享資源或狀態
- 確定性 輸入決定結果
- 可重現 能夠重現起始條件
- 排程順序不重要
併發程式:在多個程式間有資源共享
- 不確定性
- 不可重現
併發程式的正確性
- 執行過程是不確定性和不可重現的
- 程式錯誤可能是間歇性發生的
程式併發執行的好處
程式需要與計算機中的其他程式和裝置進行協作
- 共享資源
- 多個使用者使用一臺計算機
- 銀行賬戶存款餘額在多臺ATM機操作
- 機器人上的手臂和手的動作
- 加速
- I/O操作和CPU計算可用重疊(並行)
- 程式可劃分成多個模組放在多個處理器上並行執行
- 模組化
- 將大程式分解成小程式
- 以編譯為例,gcc會呼叫cpp,cc1,cc2
- 是系統易於複用和擴充套件
- 將大程式分解成小程式
可能導致的錯誤
原子操作
Atomic Operation
原子操作是一次不存在任何中斷或失敗的操作
- 要麼操作完成
- 或者操作沒有執行
- 不會出現部分執行的狀態
作業系統利用同步機制在併發執行的同時,保證一些操作是原子操作
10.2 現實生活中的同步問題
作業系統和現實生活的問題類比
- 利用現實生活問題幫助理解作業系統同步問題
- 同時注意,計算機與人的差異
例如:家庭採購協調
如何保證家庭協調的成功和高效
- 有人去買
- 需要採購時,有人去買麵包
- 最多隻有一個人去買麵包
方案一
- 使用便籤來避免購買太多面包
- 購買之前留下一張便籤
- 買完後移除
- 別人看到就不去購買
分析
麵包還是買多了
方案二
先留便籤,後檢查麵包和便籤
會導致不會有人買麵包
方案三
為便籤增加標識,以區別不同人的便籤
還是會導致有人不買
方案四
兩個人採用不同的處理流程
- 有效,但複雜
- 很難驗證其有效性
- A和B的程式碼不同
- 每個程式的程式碼也會略有不同
- 如果程式更多,怎麼辦?
- 當A在等待時,它不能做其他事
- 忙等待(busy-waiting)
方案五
利用兩個原子操作實現一個鎖(lock)
- Lock.Acquire()
- 在鎖被釋放前一直等待,然後獲得鎖
- 如果兩個執行緒都在等待同一個鎖,並且同時發現鎖被釋放了,那麼只有一個能夠獲得鎖
- Lock.Relese()
- 解鎖並喚醒任何等待種的程式
程式的互動關係:相互感知程度
- 互斥(mutual exclusion)
- 一個程式佔用資源,其它程式不能使用
- 死鎖(deadlock)
- 多個程式各佔用部分資源,形成迴圈等待
- 飢餓(starvation)
- 其他程式可能輪流佔用資源,一個程式一直得不到資源
10.3 臨界區
臨界區(Critical Section):程式中訪問臨界資源的一段需要互斥執行的程式碼
進入區(entry section):
- 檢查可否進入臨界區的一段程式碼
- 如可進入,設定相應“正在訪問臨界區”標誌
退出區(exit section):
- 清除“正在訪問臨界區”標誌
剩餘區(remainder section)
- 程式碼中其餘部分
臨界區的訪問規則
- 空閒則入
- 沒有程式在臨界區,任何程式可進入
- 忙則等待
- 有程式在臨界區,其他程式均不能進入臨界區
- 有限等待
- 等待進入臨界區的程式不能無限期等待
- 讓權等待(可選)
- 不能進入臨界區的程式,應釋放CPU(如轉換到阻塞狀態)
臨界區實現方法
- 禁用硬體中斷
- 軟體同步方法
- 更高階的抽象方法
不同的臨界區實現機制的比較
- 效能:併發級別
10.4 禁用硬體中斷
- 沒有中斷,沒有上下文切換,因此沒有併發執行
- 硬體將中斷處理延遲到中斷被啟用之後
- 現代計算機體系結構都提供指令來實現禁用中斷
- 進入臨界區
- 禁止所有中斷,並儲存標誌
- 離開臨界區
- 使能所有中斷,並恢復標誌
缺點
- 禁用中斷後,程式無法被停止
- 整個系統都會為此停下來
- 可能導致其他程式處於飢餓狀態
- 臨界區可能很長
- 無法確定響應中斷所需的時間(可能存在硬體影響)
- 要小心使用
10.5 軟體同步方法
第一次嘗試
滿足“忙則等待”,但是有時不滿足“空閒則入”
- Ti不在臨界區,Tj想要繼續執行,但是必須等待Ti進入過臨界區後
- Ti可能會被阻塞,所以Tj就會一直等待,不滿足空閒則入
第二次嘗試
不滿足忙則等待
第三次嘗試
滿足忙則等待,不滿足空閒則入
Peterson演算法
- 滿足執行緒Ti和Tj之間互斥的經典的基於軟體的解決方法
Dekkers演算法
N執行緒的軟體方法
Eisenberg和McGuire
分析
- 複雜
- 需要兩個程式間的共享資料項
- 需要忙等待
- 浪費CPU時間
10.5 更高階的抽象方法
硬體提供了一些同步原語
- 中斷禁用,原子操作指令等
作業系統提供更高階的變成抽象來簡化程式同步
- 例如:鎖、訊號量
- 用硬體原語來構建
鎖(lock)
鎖是一個抽象資料結構
-
是一個二進位制變數(鎖定/解鎖)
-
Lock::Acquire()
鎖被釋放前一直等待,然後得到鎖
-
Lock::Release()
釋放鎖,喚醒任何等待的程式
使用鎖來控制臨界區訪問
原子操作指令
現代CPU體系結構都提供一些特殊的原子操作指令
- 測試和置位(Test-and-Set)指令
- 從記憶體單元中讀值
- 測試該值是否為1(然後返回真或假)
- 記憶體單元值設定為1
-
交換指令(exchange)
- 交換記憶體中的兩個值
自旋鎖
使用TS指令實現自旋鎖(spinlock)
執行緒在等待的時候消耗CPU時間
無忙等待鎖
schedule()放棄佔用CPU,讓CPU排程其他程式
wakeup(t)喚醒執行緒,佔用資源
原子操作指令鎖的特徵
優點
- 適用於單處理器或者共享主存的多處理器中任意數量的程式同步
- 簡單並且容易證明
- 支援多臨界區
缺點
- 如果是忙等待鎖會佔用CPU時間
- 可能導致飢餓
- 程式離開臨界區時有多個等待程式的情況
- 死鎖
- 擁有臨界區的低優先順序程式,請求訪問臨界區的高優先順序程式獲得處理器並等待臨界區
總結
- 鎖是一種高階的同步抽象方法
- 互斥可以使用鎖來實現
- 需要硬體支援
- 常用的三種同步實現方法
- 禁用中斷(僅限於單處理)
- 軟體方法(複雜)
- 原子操作指令(單處理器或多處理器均可)
11. 訊號量與管程
11.1 訊號量
併發問題
- 多執行緒併發導致資源競爭
同步概念
- 協調多執行緒對共享資料的訪問
- 任何時刻只能有一個執行緒執行臨界區程式碼
確保同步正確的方法
- 底層硬體支援
- 高層次的程式設計抽象
基本同步方法
訊號量的定義
semaphore:訊號量
訊號量是作業系統提供的一種協調共享資源訪問的方法
- 軟體同步是平等執行緒間的一種同步協商機制
- OS是管理者,地位高於程式
- 用訊號量標識系統資源的數量
由Dijkstra在20世紀60年代提出,早期的作業系統的主要同步機制
- 現在很少用---但還是非常重要在電腦科學研究
訊號是一種抽象的資料型別
- 由一個整型(sem)變數和兩個原子操作P和V
- P()(Prolaag(荷蘭語嘗試減少)
- sem減一
- 如sem<0,進入等待,否則繼續
- V()(Verhoog(荷蘭語增加))
- sem加1
- 如sem<=0,喚醒一個等待程式
訊號量與鐵路的類比
- 2個站臺的車站
- 2個資源的訊號量
訊號量是被保護的整數變數
- 初始化完成後,只能通過P()和V()操作修改
- 由作業系統保證,PV操作是原子操作
P()可能阻塞,V()不會阻塞
- P操作可能沒有資源而進入等待狀態
通常假定訊號量是“公平的”
- 執行緒不會被無限期阻塞在P()操作
- 假定訊號量等待按先進先出排隊
自旋鎖不能實現先進先出
訊號量的實現
訊號量的使用
訊號量分類
- 二進位制訊號量:資源數目為0或1
- 資源訊號量:資源數目為任何非負值
- 兩者等價:基於一個可以實現另一個
訊號量的使用
-
互斥訪問
-
臨界區的互斥訪問控制
-
-
條件同步
-
執行緒間的時間等待
-
生產者-消費者問題
有界緩衝區的生產者-消費者問題描述
- 一個或多個生產者在生成資料後放在一個緩衝區裡
- 單個消費者從緩衝區取出資料處理
- 任何時刻只能有一個生產者或消費者可訪問緩衝區
問題分析
- 任何時候只能有一個執行緒操作緩衝區(互斥訪問)
- 緩衝區空時,消費者必須等待生產者(條件同步)
- 緩衝區滿時,生產者必須等待消費者(條件同步)
用訊號量描述每個約束
- 二進位制訊號量 mutex
- 資源訊號量 fullBuffers
- 資源訊號量 emptyBuffers
PV操作不能調換,檢查緩衝區是否有空地和佔有緩衝區的操作不能調換
因為一旦先佔有緩衝區,那就無法釋放緩衝區,導致死鎖現象
阻塞不代表會釋放資源,資源的釋放是通過對變數的加減來進行的。
使用訊號量的困難
- 讀開發程式碼比較困難
- 程式設計師需要能運用訊號量機制
- 容易出錯
- 使用訊號量已經被另一個執行緒佔用
- 忘記釋放訊號量
- 不能處理死鎖問題(只能在寫程式的時候解決)
11.3 管程
Moniter
管程是一種多執行緒互斥訪問共享資源的程式結構
- 採用物件導向方法,簡化了執行緒間的同步控制
- 任一時刻最多隻有一個執行緒執行管程程式碼
- 正在管程中的執行緒可臨時放棄管程的互斥訪問,等待事件出現時恢復
管程的使用
- 在物件/模組,收集相關共享資料
- 定義訪問共享資料的方法
管程的組成
-
一個鎖
- 控制管程程式碼的互斥訪問
-
0個或者多個條件變數
- 管理共享資料的併發訪問
條件變數(Condition Variable)
- 條件變數是管程內的等待機制
- 進入管程的執行緒因資源被佔用進入等待狀態
- 每個條件變數表示一種等待原因,對應一個等待佇列
- wait()操作
- 將自己阻塞在等待佇列中
- 喚醒一個等待者或釋放管程的互斥訪問
- signal()操作
- 將等待佇列中的一個執行緒喚醒
- 如果等待佇列為空,則等同空操作
條件變數實現
wait操作中會釋放對資源的佔用,程式返回回來之後還會再次佔據lock
生產者-消費者問題
因為wait會釋放對資源的佔用,所以檢查緩衝區和資源的佔用的順序可用調換,不會造成死鎖的現象
條件變數的釋放處理方式
Hansen管程:主要用於真實OS、Java中
Hoare管程:主要見於教材中
11.4 經典同步問題
哲學家就餐問題
問題描述:
-
5個哲學家圍繞一張圓桌而坐
- 桌子上放著5支叉子
- 每兩個哲學家之間放一支
-
哲學家的動作包括思考喝進餐
- 進餐時需要同時拿到左右兩邊的叉子
- 思考時將兩隻叉子放回原處
-
如何保證哲學家們的動作有序進行?
如:不出現有人永遠拿不到叉子?
方案一
方案二
方案三
讀者-寫者問題
問題描述
- 共享資料的兩類使用者
- 讀者:只讀取資料,不修改
- 寫者:讀取和修改資料
- 對共享資料的讀寫
- 讀-讀允許:允許多個讀者同時讀
- 讀-寫互斥
- 寫-寫互斥
用訊號量解決讀者-寫者問題
用訊號量描述每個約束
- 訊號量 WriteMutex
- 控制讀寫操作的互斥
- 初始化為1
- 讀者計數 Rcount
- 正在進行讀操作的讀者數目
- 初始化為0
- 訊號量 CountMutex
- 控制對讀者計數的互斥修改
- 初始化為1
- 讀者優先策略
- 只要有讀者正在讀狀態,後來的讀者都能直接進入
- 如讀者持續不斷進入,則寫者就處於飢餓
- 寫者策略
- 只要有寫者就緒,寫者應儘快執行寫操作
- 如寫者持續不斷就緒,則讀者就處於飢餓狀態
用管程解決讀者-寫者問題
寫者優先
判斷AW+WW是因為採用了Hansen管程,在返回途中可能被寫操作搶先了
12. 死鎖和程式通訊
12.1 死鎖
由於競爭資源或通訊關係,兩個或更多執行緒在執行中出現,永遠相互等待只能由其他程式引發的事件
死鎖示例
程式訪問資源的流程
- 資源型別\(R_1,R_2, ...,R_m\)
- CPU執行時間、記憶體空間、I/O裝置
- 每類資源\(R_i\)有\(W_i\)個例項
- 程式訪問資源的流程
- 請求/獲取:申請空閒資源
- 使用/佔用:程式佔用資源
- 釋放
資源分類
-
可重用資源
- 資源不能被刪除且在任意時刻只能有一個程式使用
- 程式釋放資源後,其他程式可重用
- 可重用資源示例
- 硬體:處理器、I/O通道、主和副儲存器、裝置等
- 軟體:檔案、資料庫和訊號量等資料結構
- 可能出現死鎖
- 每個程式佔用一部分資源並請求其他資源
-
消費資源
- 資源建立和銷燬
- 消耗資源示例
- 在I/O緩衝區的中斷、訊號、訊息等
- 可能出現死鎖
- 程式間相互等待接收對方的訊息
資源分配圖
描述資源和程式間的分配和佔用關係的有向圖
出現死鎖的必要條件
- 互斥
- 任何時刻只能有一個程式使用一個資源例項
- 持有並等待
- 程式至少保持一種資源,並正在等待獲取其他程式持有的資源
- 非搶佔
- 資源只能在程式使用後資源釋放
- 迴圈等待
12.2 死鎖處理方法
死鎖預防(Deadlock Prevention)
- 確保系統永遠不會進入死鎖狀態
死鎖避免(Deadlock Avoidance)
- 在使用前進行判斷,只允許不會出現死鎖的程式請求資源
死鎖檢測和恢復(Deadlock Detection & Recover)
- 在檢測到執行系統進入死鎖狀態後,進行恢復
由應用程式處理死鎖
- 通常作業系統忽略死鎖
- 大多數作業系統(包括UNIX)的做法
死鎖預防
限制申請方式
預防是採用某種策略,限制併發程式對資源的請求,使系統在任何時刻都不滿足死鎖的必要條件。
- 互斥
- 把互斥的共享資源封裝成可同時訪問
- 持有並等待
- 程式請求資源時,要求它不持有任何其他資源
- 僅允許程式在開始執行時,一次請求所有需要的資源
- 資源利用率低
- 非搶佔
- 如程式請求不能立即分配的資源,則釋放已佔有資源
- 只在能夠同時獲取所有需要資源時,才執行分配操作
- 迴圈等待
- 對資源排序,要求程式按順序請求資源
死鎖避免
利用額外的先驗資訊,在分配資源時判斷是否會出現死鎖,只在不會死鎖時分配資源
-
要求程式宣告需要資源的最大數目
-
限定提供與分配的資源數目,確保滿足程式的最大需求
-
動態檢查資源分配狀態,確保不會出現環形等待
系統資源分配的安全狀態
- 當程式請求資源時,系統判斷分配後是否處於安全狀態
- 系統處於安全狀態
- 針對所有已佔用程式,存在安全序列
安全狀態與死鎖的關係
12.3 銀行家演算法
銀行家演算法是一個避免死鎖產生的演算法。以銀行借貸分配策略為基礎,判斷並保證系統處於安全狀態
- 客戶在第一次申請貸款時,宣告所需最大資金量,在滿足所有貸款要求並完成專案時,即使規劃
- 在客戶貸款數量不超過銀行擁有的最大值時,銀行家儘量滿足客戶要求
- 類比
- 銀行家 → 作業系統
- 資金 → 資源
- 客戶 → 申請資源的程式
資料結構
n = 執行緒數量,m = 資源型別數量
安全狀態判斷演算法
當前的剩餘資源是否可以滿足其中一個執行緒的未來需要
遍歷完成實際是找到一個安全序列
銀行家演算法
判斷示例
不安全的
12.4 死鎖檢測
- 允許系統進入死鎖狀態
- 維護系統的資源分配圖
- 定期呼叫死鎖檢測演算法來搜尋圖中是否存在死鎖
- 出現死鎖時,用死鎖恢復機制進行恢復
資料結構
- Available:長度為m的向量,每種型別可用資源的數量
- Allocation:一個n×m矩陣,當前分配給各個程式每種型別資源的數量
檢測演算法
檢測示例
演算法使用
- 死鎖檢測的時間和週期選擇依據
- 死鎖多久可能會發生
- 多少程式需要回滾
- 資源圖可能有多個迴圈
- 難於分辨“造成”死鎖的關鍵程式
死鎖恢復:程式終止
- 終止所有的死鎖程式
- 一次只終止一個程式直到死鎖消除
- 終止程式的順序:
- 程式的優先順序
- 程式的已執行時間以及還需執行時間
- 程式已佔用資源
- 程式完成需要的資源
- 終止程式的數目
- 程式是互動還是批處理
死鎖恢復:資源搶佔
- 選擇被搶佔程式
- 最小成本目標
- 程式回退
- 返回到一些安全狀態,重啟程式到安全狀態
- 可能會出現飢餓
- 同一程式可能一直被選為被搶佔者
12.5 程式通訊
IPC,Inter-Process Communication
- 程式通訊是程式進行通訊和同步的機制
- IPC提供兩個基本操作
- 傳送操作:send(message)
- 接收操作:receive(message)
- 程式間通訊
- 在通訊程式間建立通訊鏈路
- 通過send/receive交換資訊
- 程式鏈路特徵
- 物理(如共享記憶體,硬體匯流排)
- 邏輯(如邏輯屬性)
通訊方式
直接通訊
- 程式必須正確的命名對方
- send(P,message)傳送資訊到程式P
- receive(Q,message)從程式Q接收訊息
- 通訊鏈路的屬性
- 自動建立鏈路
- 一條鏈路恰好對應一對通訊程式
- 每隊程式之間只有一個連結存在
- 連結可以是單向和雙向的
間接通訊
-
通過作業系統維護的訊息佇列實現程式間的訊息接收和傳送
- 每個訊息佇列都有一個唯一的標識
- 只有共享了相同訊息佇列的程式,才能夠通訊
-
通訊鏈路的屬性
- 只有共享了相同訊息佇列的程式,才建立連線
- 連線可以是單向和雙向的
- 訊息佇列可以與多個程式相關聯
- 每隊程式可以共享多個訊息佇列
-
通訊流程
- 建立一個新的訊息佇列
- 通過訊息佇列傳送和接收訊息
- 銷燬訊息佇列
-
基本通訊操作
- send(A,message)傳送訊息到佇列A
- receive(A,message)從佇列A接收訊息
阻塞與非阻塞通訊
同步與非同步通訊
阻塞通訊
- 阻塞傳送:傳送者在傳送訊息後進入等待,直到接收者成功收到
- 阻塞接收:接收方在請求接收訊息後進入等待,直到成功收到一個訊息
非阻塞通訊
- 非阻塞傳送:傳送者在傳送訊息後,可立即進行其他操作
- 非阻塞接收:沒有訊息傳送時,接收者在請求接收訊息後,接收不到任何訊息
通訊鏈路緩衝
緩衝方式
- 0容量:傳送方必須等待接收方
- 有限容量:通訊鏈路緩衝佇列滿時,傳送方必須等待
- 無限容量:傳送方無需等待
12.6 訊號和管道
訊號
訊號的定義
程式間的軟體中斷通知和處理機制
- 如:SIGKILL,SIGSTOP,SIGCONT等
訊號的接收處理
- 捕獲(catch):執行程式指定的訊號處理函式被呼叫
- 忽略(Ignore):執行作業系統指定的預設處理
- 例如:程式終止、程式掛起等
- 遮蔽(Mask):禁止程式接收和處理訊號
- 可能是暫時的(當處理同樣型別的訊號)
不足
- 傳送的資訊量小,只有一個訊號型別
訊號的實現
使用示例
管道
程式間基於記憶體檔案的通訊機制
- 子程式從父程式繼承檔案描述符
- 預設檔案描述符:
0 stdin
1 stdout
2 stderr
程式不知道(或不關心)的另一端
- 可能從鍵盤、檔案、程式讀取
- 可能寫入到終端、檔案、程式
與管道相關的系統呼叫
-
讀管道:
read(fd, buffer, nbytes)
scanf()
基於它實現的
-
寫管道:
write(fd, buffer, nbytes)
printf()
基於它實現的
-
建立管道:
pipe(rgfd)
rgfd
是2個檔案描述符組成的陣列rgfd[0]
是讀檔案描述符rgfd[1]
是寫檔案描述符
管道示例
12.7 訊息佇列和共享記憶體
訊息佇列
訊息佇列是由作業系統維護的以位元組序列為基本單位的間接通訊機制
- 訊息是一個位元組序列
- 相同標識的訊息組成按先進先出順序組成一個訊息佇列(message queues)
訊息佇列的系統呼叫
-
msgget(key, flags)
獲取訊息佇列標識
-
msgsnd(QID, buf, size, flags)
傳送訊息
-
msgrcv(QID, buf, size, type, flags)
接收訊息
-
msgctl(...)
訊息佇列控制
共享記憶體
共享記憶體是把同一個實體記憶體區域同時對映到多個程式的記憶體地址空間的通訊機制
程式
- 每個程式都有私有記憶體地址空間
- 每個程式的記憶體地址空間需明確設定共享記憶體段
執行緒
- 同一程式中的執行緒總是共享相同的記憶體地址空間
優點
- 快速方便地共享資料
不足
- 必須用額外的同步機制來協調訪問資料
共享記憶體系統呼叫
-
shmget(key, size, flags)
建立共享段
-
shmat(shmid, *shmaddr, flags)
把共享段對映到程式地址空間
-
shmdt(*shmaddr)
取消共享段到程式地址空間
-
shmctl(...)
共享段控制
-
需要訊號量等機制協調共享記憶體的訪問衝突
13. 檔案系統
13.1 檔案系統的概念
檔案系統和檔案
檔案系統是作業系統中管理永續性資料的子系統,提供資料儲存和訪問功能。
- 組織、檢索、讀寫訪問功能
- 大多數計算機系統都有檔案系統
- Google也是一個檔案系統
檔案是具有符號名,由位元組序列構成的資料項集合
- 檔案系統的基本資料單位
- 檔名是檔案的表示符號
檔案系統的功能
-
分配檔案磁碟空間
- 管理檔案塊(位置和順序)
- 管理空閒空間(位置)
- 分配演算法(策略)
-
管理檔案集合
- 定位:檔案及其內容
- 命名:通過名字找到檔案
- 檔案系統結構:檔案組織方式
-
資料可靠和安全
- 安全:多層次保護資料安全
- 可靠
- 持久儲存檔案
- 避免系統崩潰,媒體錯誤,攻擊等
檔案屬性
- 名稱、型別、位置、大小、保護、建立者、建立時間、最近修改時間...
檔案頭:檔案系統後設資料中的檔案資訊
- 檔案屬性
- 檔案儲存位置和順序
檔案描述符
檔案訪問模式
- 程式訪問檔案資料前必須先“開啟”檔案
核心跟蹤程式開啟的所有檔案
- 作業系統為每個程式維護一個開啟檔案表
- 檔案描述符是開啟檔案的標識
作業系統在開啟檔案表中維護的開啟檔案狀態和資訊
- 檔案指標:最近一次讀寫位置,每個程式分別維護自己的開啟檔案指標
- 檔案開啟計數:當前開啟檔案的次數,最後一個程式關閉檔案時,將其從開啟檔案表中移除
- 檔案的磁碟位置:快取資料訪問位置
- 訪問許可權:每個程式的檔案訪問模式資訊
檔案的使用者檢視和系統檢視
檔案的使用者檢視
- 持久的資料結構
系統訪問介面
- 位元組序列的集合(UNIX)
- 系統不關心儲存在磁碟上的資料結構
作業系統的檔案檢視
- 資料塊的集合
- 資料塊是邏輯儲存單元,而扇區是物理儲存單元
- 塊大小<>扇區大小(大小可能不一樣)
使用者檢視到系統檢視的轉換
程式讀檔案
- 獲取位元組所在的資料塊
- 返回資料塊內對應部分
程式寫檔案
- 獲取資料塊
- 修改資料塊中對應部分
- 寫回資料塊
檔案系統中的基本操作單位是資料塊
訪問模式
- 作業系統需要了解程式如何訪問檔案
- 順序訪問:按位元組一次讀取
- 大多數的檔案訪問都是順序訪問
- 隨機訪問:從中間讀取?
- 不常用,但重要
- 例如:虛擬記憶體中把記憶體頁儲存在檔案
- 不常用,但重要
- 索引訪問:依據資料特徵索引
- 通常作業系統不完整提供索引訪問
- 資料庫是建立在索引內容的磁碟訪問上
索引檔案示例
檔案內部結構
- 無結構
- 單詞、位元組序列
- 簡單記錄結構
- 分列
- 固定長度
- 可變長度
- 複雜結構
- 格式化文件(Word PDF)
- 可執行檔案等
檔案共享和訪問控制
- 多使用者系統中的檔案共享是很必要的
- 訪問控制
- 每個使用者能夠獲得哪些檔案的哪些訪問許可權
- 訪問模式:讀、寫、執行、刪除、列表等
- 檔案訪問控制列表(ACL)
- <檔案實體,許可權>
- 檔案訪問控制列表(ACL)
- <使用者|組|所有人,讀|寫|可執行>
- 使用者標識ID
- 識別使用者,表明每個使用者所允許的許可權及保護模式
- 組標識ID
- 允許使用者成組,並指定了組訪問許可權
語義一致性
- 規定多程式如何同時訪問共享檔案
- 與同步演算法相似
- 因磁碟I/O和網路延遲而設計簡單
- UNIX檔案系統(UFS)語義
- 對開啟檔案的寫入內容立即對其他開啟同一檔案的其他使用者可見
- 共享檔案指標允許多使用者同時讀取和寫入檔案
- 會話語義
- 寫入內容只有當檔案關閉時可見
- 讀寫鎖(一些基本的互斥訪問鎖,使用者程式選擇)
- 一些作業系統和檔案系統提供該功能
目錄、檔案別名和檔案系統種類
- 檔案以目錄的方式組織起來
- 目錄是一類特殊的檔案
- 目錄的內容是檔案索引表<檔名,指向檔案的指標>
- 目錄和檔案的樹形結構
- 早期的檔案系統是扁平的(只有一層目錄)
目錄操作
典型目錄操作
- 搜尋檔案
- 建立檔案
- 刪除檔案
- 列目錄
- 重新命名檔案
- 遍歷路徑
作業系統應該只允許核心修改目錄
- 確保對映的完整性
- 應用程式通過系統呼叫訪問目錄
目錄實現
- 檔名的線性列表,包涵了指向資料塊的指標
- 程式設計簡單
- 執行耗時
- 雜湊表——雜湊資料結構的線性表
- 減少目錄搜尋時間
- 衝突——兩個檔名的雜湊值相同
- 固定大小
檔案別名
兩個或多個檔名關聯同一個檔案
硬連結:多個檔案項指向一個檔案
軟連結:以”快捷方式“指向其他檔案
- 通過儲存真實檔案的邏輯名稱來實現
若刪除檔案,硬連結的檔案還是會在,軟連結的檔案就不在了
檔案目錄中的迴圈
名字解析(路徑遍歷)
- 名字解析:把邏輯名字轉換成物理資源
- 依據路徑名,在檔案系統中找到實際檔案位置
- 遍歷檔案目錄直到找到目標檔案
-
當前工作目錄(PWD)
-
每個程式都會指向一個檔案目錄用於解析檔名
-
允許使用者指定相對路徑來代替絕對路徑
如,用PWD = ”/bin“ 能夠解析 ”ls“
-
檔案系統掛載
- 檔案系統需要先掛載才能被訪問
- 未掛載的檔案系統被掛載在掛載點
檔案系統種類
- 磁碟檔案系統
- 檔案儲存在資料儲存裝置上,如磁碟
- 例如:FAT,NTFS,ext2/3,ISO9660等
- 資料庫檔案系統
- 檔案特徵是可被定址(辨識)的
- 例如:WinFS
- 日誌檔案系統
- 記錄檔案系統的修改/事件
- 網路檔案系統
- 例如:NFS,SMB,AFS,GFS
- 特殊/虛擬檔案系統
網路/分散式檔案系統
- 檔案可以通過網路被共享
- 檔案位於遠端伺服器
- 客戶端遠端掛載伺服器檔案系統
- 標準系統檔案訪問被轉換成遠端訪問
- 標準檔案共享協議
- NFS for UNIX,CIFS for Windows
- 分散式檔案系統的挑戰
- 客戶端和客戶端上的使用者辨別起來很複雜
- NFS是不安全的
- 一致性問題
- 錯誤處理模式
- 客戶端和客戶端上的使用者辨別起來很複雜
13.2 虛擬檔案系統
檔案系統的實現
- 分層結構
- 虛擬(邏輯)檔案系統(VFS,Virtual File System)
- 特定檔案系統模組
- 目的
- 對所有不同檔案系統的抽象
- 功能
- 提供相同的檔案和檔案系統介面
- 管理所有檔案和檔案系統關聯的資料結構
- 高效查詢例程,遍歷檔案系統
- 與特定檔案系統模組的互動
檔案系統基本資料結構
- 檔案卷控制塊(Unix: "superblock")
- 每個檔案系統一個
- 檔案系統詳細資訊
- 塊、塊大小、空餘塊、計數/指標等
- 檔案控制塊(Unix: "vnode" or "inode")
- 每個檔案一個
- 檔案詳細資訊
- 訪問許可權、擁有者、大小、資料塊位置等
- 目錄項(Linux: "dentry")
- 每個目錄項一個(目錄和檔案)
- 將目錄項資料結構及樹形佈局編碼成樹形資料結構
- 指向檔案控制塊、父目錄、子目錄等
檔案系統的儲存結構
檔案系統資料結構
- 卷控制塊(每個檔案系統一個)
- 檔案控制塊(每個檔案一個)
- 目錄節點(每個目錄項一個)
持久儲存在外存中
- 儲存裝置的資料塊中
當需要時載入進記憶體
-
卷控制模組:當檔案系統掛載時進入記憶體
-
檔案控制塊:當檔案被訪問時進入每次
-
目錄節點:在遍歷一個檔案路徑時進入記憶體
檔案系統的儲存檢視
13.3 檔案快取和開啟檔案
多種磁碟快取位置
資料塊快取
- 資料塊按需讀入記憶體
- 提供read()操作
- 預讀:預先讀取後面的資料塊
- 資料塊使用後被快取
- 假設資料將會再次用到
- 寫操作可能被快取和延遲寫入
- 兩種資料塊快取方式
- 資料塊快取
- 頁快取:統一快取資料塊和記憶體頁
頁快取
-
虛擬頁式儲存
- 在虛擬地址空間中虛擬頁面可對映到本地外存檔案中
-
檔案資料塊的頁快取
- 在虛擬記憶體中檔案資料塊被對映成頁
- 檔案的讀/寫操作被轉換成對記憶體的訪問
- 可能導致缺頁和/或設定為髒頁
- 問題:頁置換演算法需要協調虛擬儲存和頁快取間的頁面數
檔案系統中開啟檔案的資料結構
- 檔案描述符
- 每個被開啟的檔案都有一個檔案描述符
- 檔案狀態資訊
- 目錄項、當前檔案指標、檔案操作設定等
- 開啟檔案表
- 每個程式一個程式開啟檔案表
- 一個系統級的開啟檔案表
- 有檔案被開啟時,檔案卷就不能被解除安裝
開啟檔案鎖
一些檔案系統提供檔案鎖,用於協調多程式的檔案訪問
- 強制—根據鎖保持情況和訪問需求確定是否拒絕訪問
- 勸告—程式可以查詢鎖的狀態來決定怎麼做
13.4 檔案分配
檔案大小
大多數檔案都很小
- 需要對小檔案提供很好的支援
- 塊空間不能太大
一些檔案非常大
- 必須支援大檔案(64位檔案偏移)
- 大檔案訪問需要高效
如何表示分配給一個檔案資料塊的位置和順序
分配方式
- 連續分配
- 鏈式分配
- 索引分配
指標
- 儲存效率:外部碎片等
- 讀寫效能:訪問速度
連續分配
檔案頭指定起始塊和長度
分配策略
- 最先匹配、最佳匹配
優點
- 檔案讀取表現好
- 高效的順序和隨機訪問
缺點
- 碎片
- 檔案增長問題
- 預分配?按需分配?
鏈式分配
檔案以資料塊連結串列方式儲存
檔案頭包含了第一塊和最後一塊的指標
優點
- 建立、增大、縮小很容易
- 沒有碎片
缺點
- 無法實現真正的隨機訪問
- 可靠性差
- 破壞一個鏈,後面的資料塊也就丟了
索引分配
為每個檔案建立一個索引資料塊
- 指向檔案資料塊的指標列表
檔案頭包含了索引資料塊指標
優點
- 建立、增大、縮小很容易
- 沒有碎片
- 支援直接訪問
缺點
- 當檔案很小時索引開銷
- 如何處理大檔案?(需要多個索引塊)
UFS多級索引分配
Unix File System
效果
- 提高了檔案大小的限制閾值
- 動態分配資料塊,檔案擴充套件很容易
- 小檔案開銷小
- 只為大檔案分配間接資料塊,大檔案在訪問資料塊時需要大量查詢
13.5 空閒空間管理
跟蹤記錄檔案卷中未分配的資料塊
點陣圖
連結串列
鏈式索引
13.6 冗餘磁碟陣列RAID
磁碟分割槽
通常磁碟通過分割槽來最大限度減小尋道時間
- 分割槽是一組柱面的集合
- 每個分割槽都可視為邏輯上獨立的磁碟(切換分割槽效能很差)
一個典型的磁碟檔案系統組織
檔案卷:一個擁有完整檔案系統例項的外存空間
通常常駐在磁碟的單個分割槽上
多磁碟管理
使用多磁碟可改善
- 吞吐量(通過並行)
- 可靠性和可用性(通過冗餘,資料存多份)
冗餘磁碟陣列
RAID,Redundant Array of Inexpensive Disks
- 多種磁碟管理技術
- RAID分類,如 RAID-0, RAID-1, RAID-5
冗餘磁碟陣列的實現
- 軟體:作業系統核心的檔案卷管理
- 硬體:RAID硬體控制器(I/O)
RAID-0
磁碟條帶化
把資料塊分成多個子塊,儲存在獨立的磁碟中
- 通過獨立磁碟上並行資料訪問提供更大的磁碟頻寬
RAID-1
磁碟映象
向兩個磁碟寫入,從任何一個讀取
- 可靠性成倍增長
- 讀取效能線性增加
RAID-4
帶校驗的磁碟條帶化
資料塊級的磁碟條帶化加專用的奇偶校驗磁碟
- 允許從任意一個故障磁碟中恢復
提高可靠性和讀寫效能
RAID-5
帶分散式校驗的磁碟條帶化
基於位和基於塊的磁碟條帶化
條帶化和奇偶校驗按位元組或者位
- RAID-0/4/5:基於資料塊
- RAID-3:基於位
可糾正多個磁碟錯誤的冗餘磁碟陣列
RAID-6:每組條帶快帶有兩個冗餘塊
- 允許兩個磁碟錯誤
RAID巢狀
14. I/O 子系統
14.1 I/O 特點
裝置型別
三種常見裝置介面型別
- 字元裝置
- 串列埠、鍵盤、滑鼠等
- 訪問特徵
- 以位元組為單位順序訪問
- I/O命令
- get(), put()等
- 通常使用檔案訪問介面和語義
- 塊裝置
- 磁碟驅動器、磁帶驅動器、光碟機等
- 訪問特徵
- 均勻的資料塊訪問
- I/O命令
- 記憶體對映檔案訪問
- 原始I/O或檔案系統介面
- 網路裝置
- 乙太網、無線、藍芽等
- 訪問特徵
- 格式化報文交換
- I/O命令
- send/receive網路報文
- 通過網路介面支援多種網路協議
同步與非同步I/O
同步
非同步
14.2 I/O 結構
CPU與裝置的連線
- 裝置控制器
- CPU與I/O裝置間的介面
- 向CPU提供特殊指令和暫存器
- I/O地址
- CPU用來控制I/O硬體
- 記憶體地址或埠號
- I/O指令
- 記憶體對映I/O
- CPU與裝置的通訊方式
- 輪詢、裝置中斷和DMA(IO直接到記憶體)
I/O指令和記憶體對映I/O
- I/O指令
- 通過I/O埠號訪問裝置暫存器
- 特殊的CPU指令
- out 0x21, AL
- 記憶體對映I/O
- 裝置的暫存器/儲存被對映到記憶體實體地址單元
- 通過記憶體load/store指令完成I/O操作
- MMU設定對映,硬體跳線或程式在啟動時設定地址
核心I/O結構
I/O請求生存週期
14.3 I/O 資料傳輸
程式控制I/O(PIO, programmed I/O)
- 通過CPU的in/out或者load/store傳輸所有資料
- 特點
- 硬體簡單、程式設計容易
- 消耗的CPU時間和資料量成正比
- 適用於簡單的、小型的裝置I/O
直接記憶體訪問(DMA)
- 裝置控制器可直接訪問系統匯流排
- 控制器直接與記憶體互相傳輸資料
- 特點
- 裝置傳輸資料不影響CPU
- 需要CPU參與設定
- 適用於高吞吐量I/O
例項
I/O裝置通知作業系統的機制
- 作業系統需要了解裝置狀態
- IO操作完成時間
- IO操作遇到錯誤
- 兩種方式
- CPU主動輪詢
- 裝置中斷
輪詢
- IO裝置在特點的狀態暫存器中放置狀態和錯誤資訊
- 作業系統定期檢測狀態暫存器
- 特點
- 簡單
- IO操作頻繁或不可預測時,開銷大和延遲長
裝置中斷
- 裝置中斷處理流程
- CPU在IO之前設定任務引數
- CPU在發出IO請求之後,繼續執行其他任務
- IO裝置處理IO請求
- IO裝置處理完成時,觸發CPU中斷請求
- CPU接收中斷,分發到相應中斷處理例程
- 特點
- 處理不可預測事件效果好
- 開銷相對較高
- 一些裝置可能結合輪詢和裝置中斷
- 如 :高頻寬網路裝置
- 第一個傳入資料包到達前採用中斷
- 輪詢後面的資料包直到硬體快取為空
- 如 :高頻寬網路裝置
裝置中斷IO處理流程
14.4 磁碟排程
磁碟工作機制和效能引數
傳輸時間
磁碟排程演算法
通過優化磁碟訪問請求順序來提高磁碟訪問效能
- 尋道時間是磁碟訪問最耗時的部分
- 同時會有多個在同一磁碟上的IO請求
- 隨機處理磁碟訪問請求的效能表現很差
先進先出(FIFO)演算法
- 按順序處理演算法
- 公平對待所有程式
- 在有很多程式的情況下,接近隨機排程的效能
最短服務時間優先(SSTF)
- 選擇從磁臂當前位置需要移動最少的IO請求
- 總是選擇最短尋道時間
掃描演算法(SCAN)
-
磁臂在一個方向移動,訪問所有未完成的請求,直到磁臂到達該方向上最後的磁軌
-
調換方向
-
也稱電梯演算法
迴圈掃描演算法(C-SCAN)
- 限制了僅在一個方向上掃描
- 當最後一個磁軌也被訪問過了後,磁臂返回到磁碟的另外一端再次進行
C-LOOK演算法
- 磁臂先到達該方向上最後一個請求處,然後立即反轉,而不是先到最後點路徑上的所有請求
N步掃描演算法(N-step-SCAN)
- 磁頭粘著現象(Arm Stickiness)
- SSTF、SCAN和CSCAN的演算法中,可能出現磁頭停留在某處不動的情況
- 如:程式反覆請求對某一磁軌的IO操作
- N步掃描演算法
- 將磁碟請求佇列分成長度為N的子佇列
- 按FIFO演算法依次處理所有子佇列
- 掃描演算法處理每個佇列
雙佇列掃描(FSCAN)演算法
- FSCAN是N步掃描的簡化
- 只分為兩個佇列
- FSCAN演算法
- 把磁碟IO請求分成兩個佇列
- 交替使用掃描演算法處理一個佇列
- 新生成的磁碟IO請求放入另一個佇列中,所有的新請求都將推遲到下一次掃描時處理
14.5 磁碟快取
快取
資料傳輸雙方訪問速度差異較大時,引入的速度匹配中間層
磁碟快取是磁碟扇區在記憶體中的快取區
-
磁碟快取的排程演算法很類似虛擬儲存排程演算法
-
磁碟的訪問頻率遠低於虛擬儲存中的記憶體訪問頻率
-
通常磁碟快取排程演算法會比虛擬儲存複雜
單快取與雙快取
單快取(Single Buffer Cache)
雙快取(Double Buffer Cache)
訪問頻率置換演算法
Frequency-based Replacement
問題
- 在一段密集磁碟訪問後,LFU演算法的引用計數變化無法反映當前的引用情況
演算法思路
- 考慮磁碟訪問的密集特徵,對密集引用不計數
- 在短週期內使用LRU演算法,而在長週期中使用LFU演算法