作業系統 中斷異常系統呼叫

VictorLeeLk發表於2017-11-06

一、處理器狀態

處理器由運算器、控制器、一系列暫存器和快取記憶體構成

兩類暫存器:

  • 使用者可見暫存器:高階語言編譯器通過優化演算法分配並使用之,以減少程式訪問記憶體次數
  • 控制和狀態暫存器:用於控制處理器的操作,通常由作業系統程式碼使用

常見的控制和狀態暫存器:

  • 程式計數器(PC):記錄將要取出的指令的地址
  • 指令暫存器(IR):記錄最近取出的指令
  • 程式狀態字(PSW:Program Status Word):記錄處理器的執行狀態,如條件碼、模式、控制位等資訊

作業系統具有併發和共享的特徵,因此需要實現保護和控制
需要硬體提供基本的執行機制:

  • 處理器具有特權級別,能在不同的特權級執行的不同指令集合
  • 硬體機制可將OS與使用者程式隔離

現代處理器通常將CPU狀態設計劃分為兩種、三種或四種
在程式狀態字暫存器PSW中專門設定一位,根據執行程式對資源和指令的使用許可權而設定不同的CPU狀態(IOPL)

作業系統有兩種CPU狀態:

  • 核心態:執行作業系統程式
  • 使用者態:執行使用者程式
特權指令:只能由作業系統使用、使用者程式不能使用的指令
啟動I/O,記憶體清零,修改程式狀態字,設定時鐘,允許/禁止中斷,停機
非特權指令:使用者程式可以使用的指令
控制轉移,算術運算,訪管指令,取數指令

X86支援4個處理器特權級別
特權環:R0、R1、R2和R3

  • 從R0到R3,特權能力由高到低
  • R0相當於核心態,R3相當於使用者態,R1和R2則介於兩者之間
  • 不同級別能夠執行的指令集合不同

目前大多數基於x86處理器的作業系統只用了R0和R3兩個特權級別

CPU狀態之間的轉換:

  • 使用者態 核心態:中斷/異常/陷入機制
  • 核心態 使用者態:設定程式狀態字PSW

一條特殊的指令:陷入指令(又稱訪管指令)
提供給使用者程式的介面,用於呼叫作業系統的功能(服務)
例如:int,trap,syscall,sysenter/sysexit

相關IA-32暫存器:

暫存器名稱 本章相關內容
EFLAGS 標誌位第12、13位IOPL是輸入輸出特權級位,在當前任務的特權級CPL(Current Privilege Level,CS段暫存器的一個兩位欄位,用以指明CPU的當前特權級)高於或等於輸入輸出特權級時,就可以執行像IN、OUT、INS、OUTS、STI、CLI和LOCK等指令而不會產生異常13(即保護異常),在當前任務特權級CPL為0時,POPF(從棧中彈出至標誌位)指令和中斷返回指令IRET可以改變IOPL欄位的值
GDTR 全域性描述符表暫存器GDTR(Global Descriptor Table Register),是48位暫存器,用來儲存全域性描述符表(GDT)的32位基地址和16位GDT的界限
IDTR 中斷描述符表暫存器IDTR(Interrupt Descriptor Table Register),是48位暫存器,用來儲存中斷描述符表(IDT)的32位基地址和16位IDT的界限
LDTR 區域性描述符表暫存器LDTR(Global Descriptor Table Register),是16位暫存器,儲存區域性描述符表LDT段的選擇符
TR 任務狀態暫存器TR(Task State Register)是16位暫存器,用於儲存任務狀態段TSS段的16位選擇符

二、中斷與異常機制

中斷與異常機制是CPU對系統發生的某個事件作出的一種反應
CPU暫停正在執行的程式,保留現場後自動轉去執行相應事件的處理程式,處理完成後返回斷點,繼續執行被打斷的程式(隨機,自動,可恢復)

主要作用:

  • 及時處理裝置發來的中斷請求
  • 可使OS捕獲使用者程式提出的服務請求
  • 防止使用者程式執行過程中的破壞性活動
  • 等等…
中斷的引入:為了支援CPU和裝置之間的並行操作
I/O中斷,時鐘中斷,硬體故障
異常的引入:表示CPU執行指令時本身出現的問題
系統呼叫,頁故障/頁錯誤,保護性異常,斷點指令,其他程式性異常(如算數溢位等)
類別 原因 非同步/同步 返回行為
中斷(Interrupt) 來自I/O裝置、其他硬體部件 非同步 總是返回到下一條指令
陷入(Trap) 有意識安排的 同步 返回到下一條指令
故障(Fault) 可恢復的錯誤 同步 返回到當前指令
終止(Abort) 不可恢復的錯誤 同步 不會返回

1. 中斷/異常機制的工作原理

硬體:中斷/異常響應
捕獲中斷源發出的中斷/異常請求,以一定方式響應,將處理器控制權交給特定的處理程式
軟體:中斷/異常處理程式
識別中斷/異常型別並完成相應的處理

在每條指令執行週期的最後時刻掃描中斷暫存器,檢視是否有中斷訊號

  • 若有中斷,中斷硬體將該中斷觸發器內容按規定編碼送入PSW的相應位,稱為中斷碼,通過查中斷向量表引出中斷處理程式
  • 若無中斷訊號,繼續執行下一條指令

中斷向量:一個記憶體單元,存放中斷處理程式入口地址和程式執行時所需的處理機狀態字

Linux中的中斷向量表

中斷響應示意圖

中斷處理程式:設計作業系統時,為每一類中斷/異常事件編好相應的處理程式,並設定好中斷向量表

  • 儲存相關暫存器資訊
  • 分析中斷/異常的具體原因
  • 執行對應的處理功能
  • 恢復現場,返回被事件打斷的程式

以裝置輸入輸出中斷為例:

  • 印表機給CPU發中斷訊號,CPU處理完當前指令後檢測到中斷,判斷出中斷來源,並向相關裝置發確認訊號(硬體)
  • CPU開始為軟體處理中斷做準備,處理器狀態被切換到核心態,在系統棧中儲存被中斷程式的重要上下文環境,主要是程式計數器PC、程式狀態字PSW(硬體)
  • CPU根據中斷碼查中斷向量表,獲得與該中斷相關的處理程式的入口地址,並將PC設定成該地址,新的指令週期開始時,CPU控制轉移到中斷處理程式(硬體)
  • 中斷處理程式開始工作,在系統棧中儲存現場資訊,檢查I/O裝置的狀態資訊,操縱I/O裝置或者在裝置和記憶體之間傳送資料等等(軟體)
  • 中斷處理結束時,CPU檢測到中斷返回指令,從系統棧中恢復被中斷程式的上下文環境,CPU狀態恢復成原來的狀態,PSW和PC恢復成中斷前的值,CPU開始一個新的指令週期(硬體)

2. 中斷/異常機制例項

x86處理器:

  • 中斷:由硬體訊號引發的,分為可遮蔽和不可遮蔽中斷
  • 異常:由指令執行引發的,比如除零異常,80x86處理器釋出了大約20種不同的異常,對於某些異常,CPU會在執行異常處理程式之前產生硬體出錯碼,並壓入核心態堆疊
  • 系統呼叫:異常的一種,使用者態到核心態的唯一入口
中斷控制器(PIC或APIC)
負責將硬體的中斷訊號轉換為中斷向量,並引發CPU中斷
真實模式:中斷向量表(Interrupt Vector)
存放中斷服務程式的入口地址
保護模式:中斷描述符表(Interrupt Descriptor Table)
採用門(gate)描述符資料結構表示中斷向量
門描述符:描述控制轉移的入口點
包括任務門(Task Gate),中斷門(Interrupt Gate),陷阱門(Trap Gate),呼叫門(Call Gate)
中斷門和陷阱門
指向段選擇符,給出中斷/異常程式的段內偏移量
通過中斷門後系統會自動禁止中斷,通過陷阱門則不會

x86處理器對中斷的支援1

  • 確定與中斷或異常關聯的向量i
  • 通過IDTR暫存器找到IDT表,獲得中斷描述符(表中第i項)
  • 從GDTR暫存器獲得GDT的地址,結合中斷描述符中的段選擇符,在GDT表獲取對應的段描述符,從該段描述符中得到中斷或異常處理程式所在的段基址
  • 特權級檢查:檢查是否發生了特權級的變化,如果是,則進行堆疊切換(必須使用與新的特權級相關的棧)
  • 硬體壓棧,儲存上下文環境,如果異常產生了硬體出錯碼,也將它儲存在棧中
  • 如果是中斷,清IF位
  • 通過中斷描述符中的段內偏移量和段描述符中的基地址,找到中斷/異常處理程式的入口地址,執行其第一條指令

x86處理器對中斷的支援2

三、系統呼叫機制

系統呼叫:使用者在程式設計時可以呼叫的作業系統功能
系統呼叫是作業系統提供給程式設計人員的唯一介面
使CPU狀態從使用者態陷入核心態
每個作業系統都提供幾百種系統呼叫(程式控制、程式通訊、檔案使用、目錄操作、裝置管理、資訊維護等)

系統呼叫、庫函式、API、核心函式

1. 系統呼叫機制工作原理

中斷/異常機制
支援系統呼叫服務的實現
選擇一條特殊指令:陷入指令(亦稱訪管指令)
引發異常,完成使用者態到核心態的切換
系統呼叫號和引數
每個系統呼叫都事先給定一個編號(功能號)
系統呼叫表
存放系統呼叫服務例程的入口地址

將使用者程式的引數傳遞給核心:

  • 由陷入指令自帶引數
  • 通過通用暫存器傳遞引數(常用)
  • 在記憶體中開闢專用堆疊區

高階語言視角:

#include <unistd.h>
int main() {
    char string[5] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘!’, ‘\n’};
    write(1, string, 7);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

組合語言視角:

.section .data
output:
    .ascii “Hello!\n”
output_end:
    .equ len, output_end - output

.section .text
.globl _start
_start:
    movl $4, %eax
    movl $1, %ebx
    movl $output, %ecx
    movl $len, %edx
    int $0x80
end:
    movl $1, %eax
    movl $0, %ebx
    int $0x80
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

當CPU執行到特殊的陷入指令時:

  • 中斷/異常機制:硬體保護現場,通過查中斷向量表把控制權轉給系統呼叫總入口程式
  • 系統呼叫總入口程式:儲存現場,將引數儲存在核心堆疊裡,通過查系統呼叫表把控制權轉給相應的系統呼叫處理例程或核心函式
  • 執行系統呼叫例程
  • 恢復現場,返回使用者程式

2. 基於x86處理器的Linux系統呼叫例項

陷入指令選擇128號
int $0x80
門描述符
系統初始化時:對IDT表中的128號門初始化
描述符的2、3兩個位元組:核心程式碼段選擇符
0、1、6、7四個位元組:偏移量(指向system_call())
門型別:陷阱門
DPL:3,與使用者級別相同

系統呼叫號示例

系統執行int $0x80指令:

  • 由於特權級的改變,要切換棧,使用者棧 核心棧,CPU從任務狀態段TSS中裝入新的棧指標(SS:ESP),指向核心棧
  • 使用者棧的資訊(SS:ESP)、EFLAGS、使用者態CS、EIP暫存器的內容壓棧(返回用)
  • 將EFLAGS壓棧後,復位TF,IF位保持不變
  • 用128在IDT中找到該門描述符,從中找出段選擇符裝入程式碼段暫存器CS
  • 程式碼段描述符中的基地址+陷阱門描述符中的偏移量 定位system_call()的入口地址

Linux系統呼叫執行流程

壓棧順序

中斷髮生後OS底層工作步驟:

  • 硬體壓棧:程式計數器等
  • 硬體從中斷向量裝入新的程式計數器等
  • 組合語言過程儲存暫存器值
  • 組合語言過程設定新的堆疊
  • C語言中斷服務程式執行(例:讀並緩衝輸入)
  • 程式排程程式決定下一個將執行的程式
  • C語言過程返回至彙編程式碼
  • 組合語言過程開始執行新的當前程式

    本週重點概念
    CPU狀態,核心態/使用者態,特權指令/非特權指令,中斷,異常,中斷響應,中斷向量,中斷處理程式,系統呼叫,陷入指令,系統呼叫號,系統呼叫表

相關文章