contributed by < 56han
>
必要的設定和準備工作
kernel.perf_event_paranoid 是用來決定你在沒有 root 權限下 (Normal User) 使用 perf 時,你可以取得哪些 event data。預設值是 1 ,你可以輸入
來查看權限值。一共有四種權限值:
-1 - not paranoid at all
0 - disallow raw tracepoint access for unpriv
1 - disallow cpu events for unpriv
2 - disallow kernel profiling for unpriv
4 - disallow all unpriv perf event use
The current paranoia levels are documented in a comment in the kernel source, in file kernel/events/core.c.
預設會使 perf 權限不足,所以要用以下方法開啟權限:
執行程式
執行上述程式後,可以取得一個 pid 值,再根據 pid 輸入
會出現錯誤如下:
Error:
Couldn't create thread/CPU maps: No such process
Press any key…
但是直接執行 perf top
可以看到 example 在背景執行(第三行)。
輸入 perf top -p $pid
會出現錯誤(No such process)的原因,我目前不清楚
虛擬機器通常無法完全模擬物理硬體的性能,因此在虛擬機器上運行 perf 無法獲得真實系統的性能數據。perf 需要訪問硬體性能計數器等功能,虛擬機器無法提供足夠的權限。
Linux Kernel Module 簡介
Linux kernel module 就是一個可以插入核心執行的一個小程式片段。使用時機比如說不同裝置需要不同的驅動程式,這時就可以依照目前的硬體配置動態地載入對應的模組。
建構核心模組
出現以下 error
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
You are using: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
因此需要指定正確的編譯器:在使用 make 命令時,可以明確指定使用的編譯器。例如:
掛載核心模組後,只輸入 dmesg
,會出現以下錯誤
dmesg: read kernel buffer failed: Operation not permitted
因此使用 sudo dmesg
,因為 sudo dmesg
才可以獲取 root 權限顯示核心訊息。
卸載模心模組後 dmesg 顯示核心訊息。
fibdrv
: 可輸出 Fibonacci 數列的 Linux 核心模組ls -l: to list the files and directories with file permissions for your current location.
crw: a character special file whose user class have the read, write permissions.
注意到 511 這個數字,試著對照 fibdev.c,找尋彼此的關聯?
目前尚未有想法。
insmod
當我們透過 insmod
去載入一個核心模組時,為何 module_init
所設定的函式得以執行呢?
不管是已定義 MODULE
,或是還沒定義 MODULE
,因此最後每一個模組都會使用一個 module_init()
。
module_init
巨集幫我們做 2 件事
init_module
和傳入的函式關聯起來,因為 insmod
指令實作內部會呼叫 init_module
。如此一來呼叫 init_module
就等同於呼叫我們自己寫的函式。對 module 做初始化之前,核心先做 layout_and_allocate
為載入的 module 進行記憶體配置。
像 fibdrv.ko
這樣的 ELF 執行檔案是如何「植入」到 Linux 核心?
Executable and Linking Format 簡稱為 ELF,可以表示一個 executable binary file 或是 object file。由於這次實驗,fibdrv.ko
不是能在 shell 呼叫並執行的執行檔,因此這邊專注於解釋 ELF 檔案以 object file 的觀點。
因此我們需要透過 insmod
這個程式(可執行檔)來將 fibdrv.ko
植入核心中。kernel module 是執行在 kernel space 中,但是 insmod fibdrv.ko
是一個在 user space 的程序,因此在 insmod
中應該需要呼叫相關管理記憶體的 system call,將在 user space 中 kernel module 的資料複製到 kernel space 中。
關於 Linux 核心模組的符號
閱讀 Linux 教程網
撰寫 C 語言程式時,如果需要使用某個外部的函式,通常的做法是 #include 包含該函式原型(prototype)的頭文件,然後在程式中進行呼叫。經過編譯連接後,程序就能順利呼叫該程式。但是對於核心模組來說,這種方法並不適用,因此Linux核心提供了一種機制——核心模組符號表機制。
即使用 EXPORT_SYMBOL
標簽將模組中的函式對整個核心公開,因此導出的函式不用修改核心程式碼就可以被其他核心模組所呼叫。也就是說,使用 EXPORT_SYMBOL
可以將一個函式以符號的方式導出給其他模組使用。另外,也可以使用 EXPORT_SYMBOL_GPL
進行符號導出,但只適用於包含GPL許可權的模組。(它們定義在 include/linux/export.h
)
在 kernel/module/main.c 中定義 Linux 核心如何找到符號的過程,如果沒在內建的模組找到則會利用 list_for_each_entry_rcu
逐步走訪每個已載入的核心模組並且使用 symsearch
定義相關資訊,這邊會分成兩個類型的符號表是因為會需要根據當前模組的許可證是否為符合 GPL 授權條款,如果是則需要查找 GPL 的符號表,另外還要使用 mod->state == MODULE_STATE_UNFORMED
判斷模組的狀態是否能在被設定中,若成立則代表此模組的符號表不可使用。
閱讀 The Linux Kernel documentation
核心根據模組的許可證類型,可以限制哪些模組可以加載到核心中。
許可證類型:
“GPL”,“GPL v2”,“GPL and additional rights”,“Dual SD/GPL”,“Dual MPL/GPL”,“Proprietary”
GPL 與否對於可用的符號列表有關?
EXPORT_SYMBOL
導出的符號可以被包含 GPL 許可證的模組和不包含 GPL 許可證的模組呼叫。EXPORT_SYMBOL_GPL
導出的符號只能被包含 GPL 許可證的模組呼叫,否則會報錯。strace
追蹤 Linux 核心的掛載,涉及哪些系統呼叫和子系統?指令 sudo strace insmod fibdrv.ko
會呼叫到 finit_module
又 SYSCALL_DEFINE3
會執行 load_module
這個函式。
在 Linux kernel v6.8.5 中,finit_module 系統呼叫的實作方式已變更, 呼叫 idempotent_init_module
-> init_module_from_file
-> load_module
。