作業系統 第十六章 系統服務常式
第十六章 系統服務常式 中斷簡介 中斷描述表 例外處理 中斷處理 軟體中斷 x 系統呼叫 裝置驅動程式 摘要
中斷簡介 中斷被定義為會暫停目前指令的執行而跳到特定程式去執行的事件 中斷大致可分為 4 類 外部中斷:硬體裝置觸發,例如計時器中斷 例外中斷:由CPU產生,通常是因為指令執行時發失錯誤,例如除以零 陷阱中斷:程式是先安排好的中斷,例如系統呼叫時,利用中斷來要求核心提供系統服務 軟體中斷:完全由程式所控制而非作業系統,因此不會改變CPU的執行指令順序。雖然與一般中斷的定義不同,但是在linux內通稱為軟體中斷
Intel 定義之中斷 Intel 的文件中有另一種中斷的定義。CPU 會將暫停目前指令而跳到特定程式去執行的事件分為中斷與例外 中斷 可遮罩中斷 不可遮罩中斷 可被處理器偵測之例外 失敗 陷阱 中止 程式例外 由程式中主動要求
中斷描述表 Interrupt Descriptor Table、IDT 中斷描述表是一種系統表格,主要目的是將每一個中斷或例外的向量對應至適當的中斷或例外處理器 IDT 包含三種描述器型態 任務閘門 中斷閘門 陷阱閘門
閘門描述器的格式 000 0 0 0 0 1 1 1 0 保留 P DPL 0 0 1 1 0 TSS分段選擇器 位移(16-31) 0 1 1 1 1 分段選擇器 位移(0-15) 63 48 47 44 40 32 63 48 47 44 40 37 32 63 48 47 44 40 37 32 31 16 0 31 16 0 31 16 0 陷阱閘門描述器 中斷閘門描述器 任務閘門描述器
例外處理 Linux 利用例外來達成兩個不同的目的 Linux 例外處理器的執行流程,可分為三個步驟: 傳送信號給行程告知有異常狀況發生 處理需求分頁 Linux 例外處理器的執行流程,可分為三個步驟: 在核心的堆疊內儲存大部分暫存器的內容,這個部分的程式碼是由組合語言所撰寫 利用高階語言 C 函式處理例外 藉由 ret_from_exception() 函式離開例外處理器
中斷處理 為了使中斷處理器快速地完成,Linux 將中斷割成兩半 IRQ (interrupt request) 頂半部:具急迫性的動作 底半部:不急迫的動作 IRQ (interrupt request) 每個有中斷能力的硬體裝置均有一IRQ輸出線,連接到中斷控制器的輸入端
中斷處理 中斷控制器的運作機制 If IRQ連線有訊號產生 End If Repeat Steps 1 and 2 將收到的訊號轉換成中斷向量 將中斷向量儲存在中斷控制器的I/O Port,使得CPU可借由匯流排讀取 送訊號到CPU的INTR腳位,也就是發出中斷訊號 等待CPU將回應訊號(ACK)寫入中斷控制器的I/O Port,並同時崇設INTR End If Repeat Steps 1 and 2
中斷處理 中斷處理器要執行的四個基本動作 將 IRQ 的值和暫存器的內容放在核心的堆疊內 最後跳至 ret_from_intr() 的位址終止執行
中斷處理 IDT[32+n] INT PIC IRQn_interrupt() do_IRQ(n) 中斷處理常式1 中斷處理常式2 硬體 軟體 (中斷處理)
第十六章 系統服務常式 中斷簡介 軟體中斷 x 底半部 Softirq Tasklet 系統呼叫 裝置驅動程式 摘要
第十六章 系統服務常式 中斷簡介 軟體中斷 x 系統呼叫 執行系統呼叫 參數傳遞 封裝常式 裝置驅動程式 摘要
系統呼叫 讓使用者模式下的行程能夠使用磁碟、印表機等硬體裝置,作業系統提供了一組介面稱為系統呼叫 提供介於應用程式與硬體間的介面的好處 它使得程式設計更為容易 增加系統的安全性 介面讓程式能夠獲得更佳的可攜性
執行系統呼叫 使用者行程執行系統呼叫時,CPU 會切換至核心模式,並開始執行核心函式 為了將不同的系統呼叫號碼對應至適當的系統呼叫服務常式,Linux 核心中使用了一個系統呼叫分派表
執行一個系統呼叫 … xyz() sys_xyz() { } system_call: sys_xyz() ret_from_sys_call: iret xyz() { int 0x80 使用者模式 核心模式 應用程式執行系統呼叫 在libc標準程式庫內的封裝常式 系統呼叫處理器 系統呼叫服務常式
參數傳遞 呼叫系統呼叫時,通常需要傳遞參數給作業系統 在 Intel IA-32 的架構下,使用暫存器來傳遞參數有 2 個條件必須滿足 參數的長度不能超過暫存器大小 參數的個數不能超過 6 個 long sys_open(const char* filename, int flags, int mode)
封裝常式 核心執行緒也能夠使用系統呼叫,只不過在核心內不能夠使用程式庫的函式,因此需要透過封裝常是來執行 Linux 定義六個巨集來簡化封裝常式的宣告 _syscall0 到 _syscall5 將欲傳遞的參數放入適當的暫存器內 並執行對應的系統呼叫
系統呼叫流程圖 write() 為例 清除堆疊 Call write() 將 write() 所需參數放入堆疊 將系統呼叫號碼放在暫存器中 Int 0x80 跳回使用者程式 libc 函式庫 使用者 程式碼 0xFFFFFFFF 實體記憶體位址 作業系統 空間 指派 系統呼叫服務常式 1 2 4 5 6 7 8 3 system_call() sys_call_table
第十六章 系統服務常式 中斷簡介 軟體中斷 系統呼叫 裝置驅動程式 字元與區塊裝置 核心程式環境 撰寫裝置驅動程式 摘要
裝置驅動程式 裝置驅動程式是核心之中,由一些函式與資料所組成的 I/O 裝置的軟體介面 裝置驅動程式可以被多個使用者的應用程式所同時共享 Linux 裝置驅動程式有以下幾點特徵 包含可以與硬體裝置溝通的常式,並提供作業系統統一的使用介面 裝置驅動程式是一個自訂的元件,可以動態地從作業系統中加入或移除 可以管理並控制在使用者程式與週邊裝置之間的資料傳遞 包含一段使用者所定義的核心,可以讓程式或是週邊裝置以 "/dev" 目錄下的檔案形式供其他行程使用
裝置驅動程式與系統關係圖 使用者行程 /dev/dev_file 裝置驅動程式 硬體裝置 使用者空間 核心空間
字元與區塊裝置 字元和區塊裝置是 Linux 中兩種最主要的裝置類別 字元裝置與檔案相似,利用位元組串流方式存取資料 區塊裝置則可以使用檔案系統
核心程式環境 驅動程式必須是核心的一部分,才可以服務中斷與存取裝置硬體,並且要能夠有效率地處理中斷 多個行程同時共享裝置驅動程式中的中斷或是同步區段的資料時,資料就必須要使用臨界區以避免發生錯誤 在多 CPU 的系統中無法利用關閉中斷的方式來達到互斥,所以必須要利用旋轉鎖的方式來實作臨界區
撰寫裝置驅動程式 裝置驅動程式主要分為以下幾個部分 初始函式 註冊 IRQ 與中斷處理函式 裝置操作函式 離開函式