Lec 04 系統呼叫

木木ちゃん發表於2024-11-12

Lec 04 系統呼叫

(參考來源:上海交通大學並行與分散式系統研究所+作業系統課程ppt)
Creative Commons Attribution 4.0 License

Contents

4.1 系統呼叫

  • 硬體提供了一對指令svc/eret指令在使用者態/核心態間切換
  • 系統呼叫
    (1) 使用者與作業系統之間,類似於過程呼叫的介面
    (2) 透過受限的方式訪問核心提供的服務

alt

4.1.2 AArch64下常見的Linux系統呼叫

alt

4.1.3 系統呼叫舉例

int main()
{
    write(1, "hello, world\n", 13);
    _exit(0);
}

轉換成組合語言

  .section .rodata
  .LC0:
    .string "hello, world\n"
  .text
  .align 2
  .global main
  .type main, %function
main:
  // First, call write(1, "hello, world\n", 13)!
  movq x8, #0x40		// write is system call 64
  movq x0, #0x1		// Arg1:stdout has descriptor 1
  adrp x3, .LC0	
  add x1,x3,:lo12:.LC0	// Arg2:Hello world string
  movq x2, #0xd		// Arg3:string length
  svc			// Make the system call
  // Next, call exit(0)
  movq x8, #0x5d		// _exit is system call 93
  movq x0, #0x0		// Arg1:exit status is 0
  svc 			// Make the system call

4.1.3 系統呼叫的引數傳遞(常見軟體的約定)

  • 最多允許8個引數
    (1) x0-x7暫存器
    (2) x8用於存放系統呼叫編號
  • 返回值存放於x0暫存器中

4.1.4 系統呼叫返回值與errno

  • 系統呼叫透過暫存器嚮應用傳遞返回值
    (1) 一般設定為 -errno
    (2) 庫對系統呼叫的 wrapper code 會將系統呼叫的返回值轉換為庫函式形式的返回值

  • 暫存器放不下,只能透過記憶體傳參
    (1) 將引數放在記憶體中,將指標放在暫存器中傳給核心
    (2) 核心透過指標訪問相關引數
    (3) 存在安全的隱患(後續課程會進一步介紹)

4.1.4 如何跟蹤系統呼叫

alt

4.1.5 系統呼叫流程圖

alt

4.2 系統呼叫最佳化(Virtual Dynamic Shared Object)

核心將一部分資料透過只讀的形式共享給應用,允許應用直接讀取。

4.2.1 動機

  • 系統呼叫的時延不可忽略
    (1) 尤其是呼叫非常頻繁的情況
    (2) 系統呼叫實際執行邏輯很簡單
  • 如何降低系統呼叫的時延?
    (1) 特權級切換造成的時間開銷
    (2) 如果沒有特權級切換,那麼就不需要儲存恢復狀態

4.2.2 舉例:gettimeofday

  • 核心定義
    (1) 在編譯時作為核心的一部分
  • 使用者態執行
    (2) 將gettimeofday的程式碼載入到一塊與應用共享的記憶體頁
    (3) 這個頁稱為:vDSO
    -- Virtual Dynamic Shared Object
    (4) Time 的值同樣對映到使用者態空間(只讀)
    -- 只有在核心態才能更新這個值

4.2.3 Linux的vDSO

alt

4.3 系統呼叫最佳化:FLEX-SC

4.3.1 動機

  • 如何進一步降低系統呼叫的時延?
    (1) 不僅僅是 gettimeofday()
  • "時間都去哪兒了?"
    (1) 大部分是用來做狀態的切換
    (2) 儲存和恢復狀態 + 許可權的切換
    (3) Cache pollution
  • 是否有可能在不切換狀態的情況下實現系統呼叫?

4.3.2 Flexible System Call

  • 一種新的syscall機制
    (1) 引入 system call page ,由 user & kernel 共享
    (2) 使用者程序可以將系統呼叫的請求 push 到 system call page
    (3) 核心會從system call page poll system call 請求
  • Exception-less syscall
    (1) 將系統呼叫的呼叫和執行解耦,可分佈到不同的CPU核

4.3.3 exception less system call

alt

舉例

alt

Kernel 填充syscall的返回值

4.3.4 單核上的單執行緒/多執行緒系統呼叫

  • 單核心單執行緒
    (1) 使用者應用將多個系統呼叫推至系統呼叫頁表
    (2) 切換到核心執行緒,核心執行緒從系統呼叫頁表拉取系統呼叫。
    (3) 執行完系統呼叫後切換到應用態
  • 單核心多執行緒
    (1) 執行緒1將系統呼叫推至系統呼叫頁表,並且切換到下一個執行緒,直到所有執行緒推送結束。
    (2) 下陷到核心態。核心拉取頁表中的system call。
    (3) 執行完所有系統呼叫後返回核心態。

4.4 從應用視角看作業系統抽象

alt

4.4.1 程序

1. 分時複用有限的CPU資源

alt

(1) CPU核心數量少於應用程式數量,如何執行?
(2) 單個CPU核心如何執行多個應用程式?

  • 分時複用CPU
    (1) 讓多個應用程式輪流使用處理器核心
    (2) 何時切換:作業系統決定
    -- 執行時間片(例如100ms)
    (3) 高頻切換:看起來是多個應用“同時”執行

alt

2. 應用程式與程序
  • 通常一個應用程式對應一個程序
    (1) 在shell中輸入可執行檔案的名稱
    -- shell建立新程序,可執行檔案在新程序中執行
    (2) 在圖形介面雙擊應用圖示
  • 多程序程式:應用程式亦可自行建立新程序
    (1) 建立新程序,在新程序中執行其他應用程式或與自己一樣的程式
3. 程序在作業系統中的實現:狀態資料
  • 作業系統提供程序的抽象用於管理應用程式
    (1) 程序標識號(Process ID, PID)
    (2) 執行狀態
    -- 處理器上下文(CPU Context)
    (3) 地址空間
    (4) 開啟的檔案
4. 程序展示效果
  • 程序抽象為應用程式提供了“獨佔CPU”的假象
    (1) 程式開發不用考慮如何與其他程式共享CPU
    (2) 簡化程式設計
  • 程序相關的系統呼叫
    (1) 建立程序
    (2) 讓程序執行指定的程式
    (3) 退出程序
    (4) 程序間通訊

4.5 程序切換

4.5.1 處理器上下文(CPU Context)

  • 作業系統為每個程序維護處理器上下文
    (1) 包含恢復程序執行所需要的狀態
    (2) 思考:程序A執行到main函式任意一條指令,切換到程序B執行,一段時間後,再切回到程序A執行
    -- 為完成此過程,有哪些狀態需要儲存?
    (3) 具體包括:
    -- PC暫存器值,棧暫存器值,通用暫存器值,狀態暫存器值

4.5.2 程序切換的時機

  • 異常導致的上下文切換
    (1) Timer中斷(如基於時間片的多工排程)

  • 使用者執行系統呼叫並進入核心
    (1) 如:read/sleep等會導致程序阻塞的系統呼叫
    (2) 即使系統呼叫不阻塞執行,核心也可以決定執行上下文切換,而不是將控制權返回給呼叫程序

舉例1

alt

舉例2

alt

4.6 程序相關介面

4.6.1 獲取程序ID

  • 程序 ID
    (1) 每個程序都有唯一的正數PID
  • Getpid()
    (1) 返回撥用程序的PID
  • Getppid()
    (1) 返回撥用程序父程序的PID
    (2) 父程序:建立該程序的程序

alt

4.6.2 Exit 函式

alt

exit函式終止程序並帶上一個status狀態

4.6.3 Fork 函式

alt

  • 呼叫一次
    (1) 在父程序中
  • 返回兩次
    (1) 在父程序中,返回子程序的PID
    (2) 在子程序中,返回0
  • 返回值提供了唯一明確地區分父程序和子程序執行的方法

alt

alt

4.6.4 execve 函式

alt
alt

  • 載入和執行
    (1) filename:可執行檔名;argv:引數列表,envp:環境變數列表
  • execve 只呼叫一次,且永遠不會返回
    (1) 僅僅在執行報錯的時候,返回撥用程式
    (2) 例:找不到filename標識的檔案

4.6.5 Linux下的殭屍程序

  • 程序終止後,核心不會立刻銷燬該程序
    (1) 不再執行,但仍然佔用記憶體資源
  • 程序以終止態存在,等待父程序回收
  • 當父程序回收終止的子程序
    (2) 核心把子程序的exit狀態傳遞給父程序
    (3) 核心移除子程序,此時子程序才被真正回收
  • 終止狀態下還未被回收的程序就是殭屍程序
  • 如果父程序在自己終止前沒有回收殭屍子程序
    (4) 核心會安排init程序回收這些子程序
  • init程序
    (5) PID為1
    (6) 在系統初始化時由核心建立

4.6.6 waitpid函式

alt
alt

返回值:成功返回子程序 PID,出錯返回 -1

  • pid>0:等待集合中只有pid子程序
  • pid=-1:等待集合包括所有子程序
  • 如果沒有子程序:返回-1,errno = ECHILD
  • 如果等待被中斷:返回-1,errno = EINTR
  • options=0
    (1) 掛起呼叫程序,等待集合中任意子程序終止
    (2) 如果等待集合中有子程序在函式呼叫前已經終止,立刻返回
    (3) 返回值是導致函式返回的終止子程序pid
    (4) 該終止子程序被核心回收
  • options=WNOHANG
    (1) 如果等待集合中沒有終止子程序,立刻返回0
  • options=WUNTRACED
    (2) 除了返回終止子程序的資訊外,還返回因訊號而停止的子程序資訊
  • options=WNOHANG|WUNTRACED
    (1) 帶回被回收子執行緒的exit狀態
    (2) status指標不為NULL
    (3) status包含導致子程序進入終止狀態的資訊
    (4) wait.h檔案包含了若干宏定義,用於解釋status

4.6.7 小結

alt

4.7 記憶體

alt
alt

4.7.1 虛擬記憶體

  • 虛擬地址空間
    (1) 應用程序使用虛擬地址訪問記憶體
    (2) 所有應用程序的虛擬地址空間都是統一的(方便開發)

  • 地址翻譯
    (1) CPU按照OS配置的規則把虛擬地址翻譯成實體地址
    (2) 翻譯對於應用程序是不可見的(無需關心)

4.7.2 虛擬記憶體和實體記憶體

alt

虛擬記憶體具有獨立而統一的地址空間

alt

5. ELF 檔案格式

5.1 目標檔案

  • 可執行目標檔案
  • 可重定位目標檔案(.o)
  • 共享目標檔案(.so)
    (1) 特殊的可重定位目標檔案
    (2) 可以載入到記憶體中
    (3) 支援動態連結:載入時或執行時

5.2 ELF:可執行可連結格式

  • 目標檔案的標準二進位制格式
    統一格式,又被稱為ELF二進位制檔案
  • 用於BSD Unix和Linux
    最早用於AT&T System

5.3 ELF格式:可重定位目標檔案

alt

(1) ELF 頭部(ELF Header)

  • ELF檔案的第一個部分
  • 通常用於存後設資料
    -- Magic number (‘0x7f’ ‘E’ ‘L’ ‘F’)
    -- 型別(.o, .so, 可執行)
    -- 機器架構
    -- 位元組順序(大小端)
    -- 節頭部表的位置(檔案內偏移)

(2) 節頭部表(Section Header Table)

  • 節(section)
    -- ELF檔案中除了頭部和頭部表劃分為若干區域
    -- 每一個節在檔案中時一塊連續的位元組(可能為空)
    -- 互不重疊
  • 每一個節都有一個節頭部描述
    -- 節頭部的一項

alt

  • sh_name
    -- 節名稱(在.strtab節中的偏移)
  • sh_addr
    -- 節在載入到記憶體後,在記憶體的起始地址
  • sh_offset
    -- 節在檔案中的偏移(位元組數)
  • sh_size
    -- 節的大小(位元組數)
  • sh_addralign
    -- 對齊要求

(3) ELF字串表(.strtab)

  • 記錄一系列C風格字串
    -- 以'\0’結尾
  • 表示符號名或節名
    -- 使用時記錄字串在表中的偏移(index)

alt

(4) 用以除錯的節

  • .debug
    -- 除錯符號表,包括變數(全域性、區域性)、typedef、C原始檔
    -- gcc –g生成
  • .line
    -- C原始檔的行數與.text節中指令的對映

(5) 程式碼與資料節

  • .text:程式碼
  • .rodata:只讀資料
  • .data:初始化的全域性變數和靜態變數
  • .bss:未初始化的全域性變數和靜態變數
    -- “Block Started by Symbol”,“Better Save Space”
    -- 不佔檔案空間,但是在節頭部表中有記錄
    -- 執行時分配記憶體,預設為0

5.4 可執行目標檔案

alt

alt

  • ELF頭部(ELF header)
    -- 整體資訊
    -- 程式入口 (e_entry)
    -- 程式頭部表資訊
    • 在檔案中的起始位置(e_phoff)
    • 大小(e_ehsize)
    • 每個條目的大小(e_phentsize)
    • 條目數量(e_phnum)

alt

程式頭部表

  • Program (Segment) Header Table
    p_type
  • PT_LOAD (1): 可載入段
    p_flags
    -- 執行時許可權 (rwx)
  • p_offset
    -- 段在檔案中的起始偏移
  • p_filesz
    -- 段在檔案中的大小( 段在記憶體中的大小p_memsz)
  • p_vaddr
    -- 段在記憶體中的起始(虛擬)地址
  • p_paddr (不常用,x86、ARM 下不用)
  • p_memsz
    -- 段在記憶體中的大小(p_filesz)
  • p_align
    -- 段起始地址的對齊要求
    通常為2^12 (4K) 或 2^21 (2M)

alt

alt

6. 檔案

6.1 UNIX 檔案

  • Unix 檔案是一串位元組序列。
    -- \(B_0,B_1,\ldots,B_k,\ldots,B_m\)
  • 所有的IO裝置都被抽象成檔案
    -- 如:網路裝置,硬碟,終端
    -- Unix提供一個基於檔案的底層應用介面,即UNIX I/O
  • 所有輸入,輸出都是透過讀,寫檔案完成
    -- 所有的輸入輸出都具有統一的表現形式

6.2 檔案型別1

  • 普通檔案(regular file):包含任意資料
    -- 從應用程式的角度來看有兩種
    • 文字檔案:僅包含ASCII和UNICODE字元
    • 二進位制檔案:除了文字檔案以外的所有
      -- 從核心的角度來看沒有區別

6.3 檔案型別2

  • 目錄(directory)也是一個檔案
    -- 由有連結(links)構成
    -- 每個連結將一個檔名對映到一個檔案(或目錄)
    -- 每個目錄至少有兩個連結:
    • . (dot):到資料夾本身的連結
    • . . (dot-dot):到上一層資料夾的連結
      -- 目錄相關指令:mkdir、ls、rmdir等

6.4 目錄層級

  • Linux核心使用層次化目錄來組織所有檔案
    -- /:代表根目錄
    -- 每個檔案都是根目錄直接或間接的後代

alt

6.5 檔案型別3

  • 套接字(Socket)也是檔案,用於跨網路程序互動
  • 其他檔案型別包括:
    -- 命名管道(named pipes)
    -- 符號連線(symbolic links)
    -- 字元/塊裝置(character/block devices)
    ...

6.6 開啟檔案

  • 應用準備訪問一個IO裝置
    -- 核心開啟相關檔案,並返回一個非負整數,作為檔案識別符號(file descriptor, fd)
    • fd代表該檔案,用於之後對檔案進行操作
      -- 核心跟蹤記錄每個程序的所有開啟檔案的資訊
    • 對於每個開啟檔案,維護一個檔案內偏移k
    • 應用可以透過seek函式,顯式改變當前檔案內偏移k
    • 應用只需要記錄核心返回的檔案識別符號

alt

6.7 關閉檔案

  • 不再需要訪問檔案
  • 核心操作如下
    -- 釋放在檔案開啟時建立的資料結構
    -- 把檔案識別符號返回到可用的識別符號池
  • 程序終止時的預設行為
    -- 核心關閉所有開啟的檔案
    -- 核心釋放記憶體資源

alt

6.8 讀寫檔案

  • 讀操作:
    -- 從檔案中複製m>0個位元組到記憶體中
    • 從當前檔案的位置k開始,並更新k+=m
      -- 如果從k開始到檔案末尾的長度小於m,觸發條件end-of-file(EOF)
    • EOF可以被應用檢測
    • 但是檔案末尾實際上不存在EOF字元

alt

alt

6.9 檔案:對所有裝置的抽象

  • 儲存裝置
    -- File
  • 網路裝置
    -- socket
  • 其他裝置
    -- 同樣是fd

alt

6.10 作業系統裝置的示例

alt

alt

相關文章