Try   HackMD

2025q1 Homework3 (kxo)

contributed by < Nsly0204 >

先備知識摘要

作業要求

2025 年 Linux 核心設計課程作業 —— kxo (B)

Device Driver 重點整理

Linux device driver 可分成 3 種類型:

  1. character device driver
  2. block device driver
  3. network device driver

Linux device driver(以下或稱為驅動程式)扮演的角色:
userspace > system call > device driver > device file

參考: 一切皆為檔案描述子
這種統一抽象層使不同類型的 I/O (如終端輸出與檔案讀寫) 皆可視為檔案操作,裝置驅動程式的核心目的即在於實作這樣的抽象層,使作業系統透過一致介面存取各類裝置。
這些操作皆基於檔案描述子(file descriptor),如 open()、read()、write() 和 lseek()。在 UNIX 中,檔案被視為位元組序列,而所有 I/O 操作均透過這些標準介面進行。

由此觀念,可以預期一個驅動程式包含以下部份,以 ksort 為例:

  • 註冊驅動程式: 將 driver 自己「註冊」到 kernel 的 vVirtual File System(VFS) 層,通常直接實作在 __init 裡。
  • 結構體 file_operations: 提供核心收到 system call 與裝置對應的函式呼叫,其中 fops 就是指向該結構體的指標。
  • 實作函式對應 system call 的操作,如範例的main_sort

2025 年 Linux 核心設計課程作業 —— kxo ( C )

問題

  • what does slave PIC do?

Slides (L07-LinuxEvents).pdf

Programmable Interrupt Controller
Programmable Interrupt Controller (PIC) 是控制 Interrupt 的硬體架構,用於把不同的 Interrupt Request Lines (IRQs) 映射成 Interrupt Vectors 送給 CPU ,PIC 在產生 Interrupt Vectors 時就會進行優先程度的分級。

Interrupt Vectors
當外部裝置發出中斷訊號,PIC 接受的訊號會被轉換成一組 Interrupt Vectors ,用於讓 CPU 對照 Interrupt Descriptor Table (IDT) ,找到對應的 ISR / Interrupt handler 起始位址進行處理。

現代架構中,不會同時發生的 I/O devices 可以共享一個 IRQ ,也就是共享 vector ,每一個 device 的 handler / IRS 會一起被呼叫,至於最後是哪個 device 有中斷是依照各個 device 自己決定。

詳細說明可以參考先前同學的整理 Linux 核心設計: Interrupt,部份內容也是引述本篇描述。

Interrupt Handler / Interrupt Service Routine(IRS)

在現代的作業系統中,ISR 會被切成 top half 和 botton half 兩個部份,目的是為了減少任務的延遲。Top half 和 botton half 的區分使得系統可以把 interrupt 的處理推遲。

Top Half 主旨為確認中斷(先上車)且只執行不可延遲的部份,過程中屏蔽其他中斷(執行在 interrupt context 不可排程),時間須越短越好:

  • 確認中斷來源:在共享 interrupt line 的情況下,檢查硬體狀態以判斷是否為本裝置觸發的中斷。
  • Acknowledgement:向硬體發送確認訊號,表示中斷已被接收,防止重複觸發。
  • 排程後續處理:標記需延後處理的任務。
  • 其他不可延遲的關鍵任務。

Bottom Half 實作中斷後續處理,更準確的說是去完成 Top Half 也就是 Interupt Handler / IRS 不處理的剩餘事項,主要有三種實作方式:

  • softirqs
  • tasklets:所有 softirq 都由 tasklet(queue) 實現,同時只能有一個 tasklet 被處理器執行(多 CPUs也不行),tasklet 中的 irq 可以有優先級之分。
  • workqueues:必定被視為 kernel thread 執行,由於運作於 process context,workqueue允許睡眠。

注意 Top HalfBottom Half 的分別並沒有硬性規定,給予 driver 撰寫者自行分配的空間。

Softirqs

每個 CPU 都會維護自己的 softirq daemon 也就是 ksoftirqd kernel thread ,用於處理 softirq,其流程如下:

  • 在 softirq context 執行,屏蔽大部分睡眠與中斷,但可以被更高優先級的 softirq 中斷。
  • 利用 void open_softirq(softirq_id, handler) 註冊要處理的 softirq 的 handler 。
  • raise_softirq 觸發 softirq 的處理。
  • __do_softirq 讓 handler 處理,結束後呼叫 exiting_irq()

`
Workqueue

  • producer 生產數據,寫入 buffer。
  • consumer 消費數據,讀取 buffer。

使對奕畫面在使用者層級呈現

掛載 kxo 對弈模組,並嘗試執行。

$ make
$ sudo insmod kxo.ko
$ sudo ./xo-user

按下 ctrl + Q 後停止電腦 vs. 電腦的對弈。

觀察 xo-user.c 發現 userspace 直接呼叫 read 即可以獲得棋盤的輸出。

if (FD_ISSET(STDIN_FILENO, &readset)) {
    FD_CLR(STDIN_FILENO, &readset);
    listen_keyboard_handler();
} else if (read_attr && FD_ISSET(device_fd, &readset)) {
    FD_CLR(device_fd, &readset);
    printf("\033[H\033[J"); /* ASCII escape code to clear the screen */
    read(device_fd, display_buf, DRAWBUFFER_SIZE);
    display_buf[DRAWBUFFER_SIZE - 1] = '\0';
    printf("%s", display_buf);
}

搜尋 __init 可以發現 kxo.ko 模組被定義在 main.c 中,進一步搜尋 fops 找到函式 kxo_read