系統功能
| 系統 |
|---|
| 使用者空間介面,系統呼叫 |
| 驅動程式模型 |
| 模組 |
| 匯流排,PCI |
| 硬體介面,[重新]啟動 |
本文介紹了用於支援和管理其他核心功能的基礎設施。此功能以系統呼叫和 sysfs 命名。
使用者空間通訊是指使用者空間應用程式和核心之間的資料和訊息交換。使用者空間應用程式是在作業系統使用者空間中執行的程式,使用者空間是記憶體中的一個受保護區域,它為應用程式提供安全且隔離的環境以在其內執行。
Linux 中有幾種機制可用於使用者空間與核心之間的通訊。最常見的機制之一是透過系統呼叫,系統呼叫是允許使用者空間應用程式請求核心服務的函式,例如開啟檔案、建立程序和訪問系統資源。
使用者空間通訊的另一種機制是透過服務檔案,服務檔案是代表物理或虛擬裝置的特殊檔案,例如儲存裝置、網路介面和各種外圍裝置。使用者空間應用程式可以透過讀寫它們相應的裝置檔案來與這些裝置通訊。
總之,Linux 核心提供了多種使用者空間通訊機制,包括系統呼叫、裝置檔案、procfs、sysfs 和 devtmpfs。這些機制使使用者空間應用程式能夠與核心通訊並以安全且受控的方式訪問系統資源。
⚲ API
- 使用者空間的核心空間 API
- 核心空間的使用者空間 API
📖 參考
系統呼叫是使用者空間應用程式與 Linux 核心之間的基本介面。它們提供了一種方法讓程式請求作業系統的服務,例如開啟檔案、分配記憶體或建立新程序。在 Linux 核心中,系統呼叫是作為函式實現的,使用者空間程式可以使用軟體中斷機制來呼叫這些函式。
Linux 核心提供了數百個系統呼叫,每個系統呼叫都有其獨特的功能。這些系統呼叫被組織成類別,例如程序管理、檔案管理、網路通訊和記憶體管理。使用者空間應用程式可以使用這些系統呼叫來與核心互動並訪問底層系統資源。
⚲ API
⚙️ 內部結構
📖 參考
- 系統呼叫
- 系統呼叫目錄,man 手冊第 2 節
- 系統呼叫的剖析,第 1 部分 和 第 2 部分
- syscalls ltp
💾 歷史
經典的 UNIX 裝置是作為位元組流使用man 2 ioctl 的字元裝置。
⚲ API
ls /dev cat /proc/devices cat /proc/misc
示例:misc_fops id usb_fops id memory_fops id
- 分配的裝置 doc
- drivers/char src - 實際上是位元組流裝置
- 第 13 章。I/O 架構和裝置驅動程式
⚠️ 警告:混淆。hiddev 不是真正的人機介面裝置!它重新使用了 USBHID 基礎設施。hiddev 例如用於監視器控制和不間斷電源。此模組使用 /dev/usb/hiddevX 上的單獨事件介面來單獨支援這些裝置(180:96 到 180:111)(⚙️ HIDDEV_MINOR_BASE id)
⚲ API
⚙️ 內部結構
📖 參考
📖 參考
🔧 待辦事項
📖 參考
proc 檔案系統(procfs)是一個特殊的 檔案系統,它以層次檔案結構的形式呈現有關程序和其他系統資訊的資訊,提供了一種比傳統跟蹤方法或直接訪問核心記憶體更方便、更標準的方法來動態訪問核心中儲存的程序資料。通常,它在啟動時對映到名為 /proc 的掛載點。proc 檔案系統充當核心內部資料結構的介面。它可用於獲取有關係統的資訊,並在執行時更改某些核心引數。
/proc 包含一個目錄,用於每個正在執行的程序(包括核心執行緒),這些目錄命名為 /proc/PID,其中 PID 是程序號。每個目錄都包含有關一個程序的資訊,包括最初啟動該程序的命令(/proc/PID/cmdline)、其環境變數的名稱和值(/proc/PID/environ)、指向其工作目錄的符號連結(/proc/PID/cwd)、指向原始可執行檔案的另一個符號連結(如果仍然存在)(/proc/PID/exe)、兩個包含指向每個開啟的檔案描述符的符號連結的目錄(/proc/PID/fd)和每個檔案描述符的狀態(位置、標誌等)(/proc/PID/fdinfo)、有關對映檔案和塊的資訊(例如堆疊和堆)(/proc/PID/maps)、一個表示程序虛擬記憶體的二進位制映像(/proc/PID/mem)、指向程序看到的根路徑的符號連結(/proc/PID/root)、包含指向任何子程序或執行緒的硬連結的目錄(/proc/PID/task)、有關程序的基本資訊,包括其執行狀態和記憶體使用情況(/proc/PID/status),以及更多資訊。
📖 參考
sysfs
[edit | edit source]sysfs 是一個偽檔案系統,它透過虛擬檔案將有關核心子系統、硬體裝置和相關裝置驅動程式的資訊從核心的裝置模型匯出到使用者空間。除了提供有關各種裝置和核心子系統的資訊外,匯出的虛擬檔案還用於它們的配置。sysfs 旨在匯出裝置樹中存在的資訊,這樣就不會再混亂 procfs。
sysfs 掛載在 /sys 掛載點下。
⚲ API
📖 參考
devtmpfs
[edit | edit source]devtmpfs 是一個核心/使用者空間混合方法的裝置檔案系統,在 udev 首次執行之前提供節點。
📖 參考
容器化
[edit | edit source]容器化 是一項強大的技術,它徹底改變了軟體應用程式的開發、部署和執行方式。容器化的核心是為執行應用程式提供一個隔離的環境,應用程式在其中擁有所有必要的依賴關係,並且可以輕鬆地從一個環境移動到另一個環境,而無需擔心任何相容性問題。
容器化技術起源於 chroot 命令,該命令於 1979 年在 Unix 作業系統中引入。chroot 提供了一種更改程序根目錄的方法,有效地建立了一個具有自己的檔案系統層次結構的新隔離環境。但是,這種早期容器化實現的功能有限,難以管理和控制容器內執行的各種程序。
在 2000 年代初期,Linux 核心引入了 名稱空間 和 控制組,以提供更強大、更可擴充套件的容器化解決方案。名稱空間 允許程序擁有自己的系統隔離檢視,包括檔案系統、網路和程序 ID 空間,而 控制組 則對分配給每個容器的資源(如 CPU、記憶體和 I/O)提供細粒度控制。
利用這些核心功能,容器化平臺(如 Docker 和 Kubernetes)已成為大規模構建和部署容器化應用程式的流行解決方案。容器化已成為現代軟體開發中必不可少的工具,使開發人員能夠輕鬆地打包應用程式並在不同的環境中以一致且可預測的方式部署它們。
資源使用和限制
[edit | edit source]⚲ API
- man 2 chroot – 更改根目錄
- man 2 sysinfo – 返回系統資訊
- man 2 getrusage – 獲取資源使用情況
- 獲取/設定資源限制
📖 參考
名稱空間
[edit | edit source]Linux 名稱空間 提供了一種隔離和虛擬化作業系統不同方面的方法。名稱空間允許應用程式的多個例項在彼此隔離的情況下執行,而不會干擾主機系統或其他例項。
🔧 待辦事項
⚲ API
- /proc/self/ns
- man 8 lsns,man 2 ioctl_ns ↪ ns_ioctl id
- man 1 unshare,man 2 unshare
- man 1 nsenter,man 2 setns
- man 2 clone3 ↪ clone_args id
- linux/ns_common.hinc
- linux/proc_ns.hinc
- 名稱空間定義
⚙️ 內部結構
- init_nsproxy id - 名稱空間結構
- kernel/nsproxy.csrc
- fs/namespace.csrc
- fs/proc/namespaces.csrc
- net/core/net_namespace.csrc
- kernel/time/namespace.csrc
- kernel/user_namespace.csrc
- kernel/pid_namespace.csrc
- kernel/utsname.csrc
- kernel/cgroup/namespace.csrc
- ipc/namespace.csrc
📖 參考
- man 7 名稱空間
- man 7 uts_namespaces
- man 7 ipc_namespaces
- man 7 mount_namespace
- man 7 pid_namespaces
- man 7 network_namespaces
- man 7 user_namespaces
- man 7 time_namespaces
- man 7 cgroup_namespaces
控制組
[edit | edit source]控制組 用於限制和控制程序組的資源使用。它們允許管理員設定對 CPU 使用率、記憶體使用率、磁碟 I/O、網路頻寬和其他資源的限制,這對於管理系統效能和防止資源爭用非常有用。
控制組有兩個版本。與 v1 不同,控制組 v2 只有一個程序層次結構,它區分程序,而不是執行緒。
以下是控制組 v1 和 v2 之間的一些主要區別
| 控制組 v1 | 控制組 v2 | |
|---|---|---|
| 層次結構 | 每個子系統都有自己的層次結構,這會導致複雜性和混亂 | 統一的層次結構,簡化了管理,並實現了更好的資源分配 |
| 控制器 | 具有多個由獨立控制器控制的子系統,每個子系統都有自己的配置檔案和引數集 | 控制器被合併到單個 "cgroup2" 控制器中,該控制器提供了一個統一的介面來管理資源 |
| 資源分配 | 根據比例共享將資源分配給程序組,這會導致不可預測的結果 | 根據 "加權公平排隊" 演算法分配資源,這提供了更好的可預測性和公平性 |
控制組 v2 與控制組 v1 不相容,這意味著從 v1 遷移到 v2 可能會很困難,並且需要仔細的規劃。
🔧 待辦事項
⚲ API
- linux/cgroup.hinc
- linux/cgroup-defs.hinc
- css_set id – 持有指向 cgroup_subsys_state id 物件的引用計數指標集
- cgroup_subsysid
- linux/cgroup_subsys.h inc – 控制組子系統的列表
⚙️ 內部結構
- cg_list id – task_struct 中 css_set id 的列表
- kernel/cgroupsrc
- cgroup_initid
- cgroup2_fs_typeid
📖 參考
- 控制組 v1 文件
- 手冊 1 systemd-cgtop
- 手冊 5 systemd.slice – 切片單元配置
- 手冊 7 cgroups
- man 7 cgroup_namespaces
- CFS 頻寬控制用於控制組 文件
- 即時組排程 文件
📚 進一步閱讀
Linux 驅動模型(或裝置模型,或簡稱 DM)是一個框架,它為裝置驅動程式提供了一種一致且標準化的方式來與核心互動。它定義了一套規則、介面和資料結構,使裝置驅動程式能夠與核心通訊並執行各種操作,例如管理資源、生命週期等等。
DM 核心結構由 DM 類、DM 匯流排、DM 驅動程式和 DM 裝置組成。
在 Linux 核心中,kobject id 是一個基本資料結構,用於表示核心物件並提供與它們互動的標準化介面。kobject 是一個通用物件,可以表示任何型別的核心物件,包括裝置、檔案、模組等等。
kobject 資料結構包含幾個描述物件的欄位,例如它的名稱、型別、父級和操作。每個 kobject 在其父級物件中都有一個唯一的名稱,而父子關係形成一個 kobject 的層次結構。
kobject 由核心的 sysfs 檔案系統管理,該檔案系統提供一個虛擬檔案系統,將核心物件暴露為使用者空間中的檔案和目錄。每個 kobject 都與一個 sysfs 目錄相關聯,該目錄包含可以讀取或寫入以與核心物件互動的檔案和屬性。
⚲ 基礎設施 API
- linux/kobject.hinc
- kobjectid
- 核心物件操作 文件
- 🔧 待辦事項
類是裝置的更高級別的檢視,它抽象出低級別實現細節。驅動程式可能看到 NVME 儲存或 SATA 儲存,但在類級別,它們都只是 block_class id 裝置。類允許使用者空間基於裝置的功能來處理裝置,而不是它們連線的方式或工作方式。通用 DM 類結構與 組合模式 匹配。
⚲ API
- ls /sys/class/
- class_register id 註冊 class id
- linux/device/class.hinc
👁 例子:input_class id,block_class id net_class id
一個 外設匯流排 是處理器和一個或多個外設之間的一個通道。DM 匯流排是 代理 用於外設匯流排。通用 DM 匯流排結構與 組合模式 匹配。就裝置模型而言,所有裝置都透過匯流排連線,即使它是內部的、虛擬的,platform_bus_type id。匯流排可以互相連線。例如,USB 控制器通常是 PCI 裝置。裝置模型表示匯流排之間以及它們控制的裝置之間的實際連線。匯流排由 bus_type id 結構表示。它包含名稱、預設屬性、匯流排的方法、PM 操作以及驅動程式核心的私有資料。
⚲ API
- ls /sys/bus/
- bus_register id 註冊 bus_type id
- linux/device/bus.hinc
👁 例子:usb_bus_type id,hid_bus_type id,pci_bus_type id,scsi_bus_type id,platform_bus_type id
⚲ API
- ls /sys/bus/:/drivers/
- module_driver id - 簡單的通用驅動程式初始化程式,👁 例如在 module_pci_driver id 中使用
- driver_register id 註冊 device_driver id - 基本的裝置驅動程式結構,每個裝置例項一個。
- linux/device/driver.hinc
👁 例子:hid_generic id usb_register_device_driver id
平臺驅動程式
- module_platform_driver id 註冊 platform_driver id(device_driver id 的平臺包裝器)和 platform_bus_type id
- linux/platform_device.hinc
👁 例子:gpio_mouse_device_driver id
⚲ API
- ls /sys/devices/
- device_register id 註冊 device id - 基本的裝置結構,每個裝置例項一個
- linux/device.hinc
- linux/dev_printk.hinc
- 裝置資源管理 文件,devres,devm ...
👁 例子:platform_bus id mousedev_create
平臺裝置
- platform_device id - struct device - 基本的裝置結構 文件 的平臺包裝器,包含與裝置相關的資源
- 它可以由 platform_device_register_simple id 或 platform_device_alloc id 動態自動建立。或者使用 platform_device_register id 註冊。
- platform_device_unregister id - 釋放裝置和相關資源
👁 例子:add_pcspkr id
⚲ API 🔧 待辦事項
- platform_device_info platform_device_id platform_device_register_full platform_device_add
- platform_device_add_data platform_device_register_data platform_device_add_resources
- attribute_group dev_pm_ops
⚙️ 內部結構
📖 參考
⚲ API
- lsmod
- cat /proc/modules
⚙️ 內部結構
📖 參考
- LDD3:構建和執行模組
- http://www.xml.com/ldd/chapter/book/ch02.html
- http://www.tldp.org/LDP/tlk/modules/modules.html
- http://www.tldp.org/LDP/lkmpg/2.6/html/ Linux 核心模組程式設計指南
外設匯流排是用於將各種外圍裝置連線到計算機系統的通訊通道。這些匯流排用於在外圍裝置和系統的處理器或記憶體之間傳輸資料。在 Linux 核心中,外設匯流排作為驅動程式實現,這些驅動程式使作業系統與硬體之間的通訊成為可能。
Linux 核心中的外設匯流排包括 USB、PCI、SPI、I2C 等等。這些匯流排中的每一個都有其自身獨特的特性,並且 Linux 核心為各種外設提供了支援。
PCI(外設元件互連)匯流排用於連線計算機系統中的內部硬體裝置。它通常用於連線顯示卡、網絡卡和其他擴充套件卡。Linux 核心提供了 PCI 匯流排驅動程式,使作業系統與連線到匯流排的裝置之間的通訊成為可能。
USB(通用序列匯流排)是現代計算機系統中最常用的外設匯流排之一。它允許裝置進行熱插拔,並支援高速資料傳輸速率。
🔧 待辦:裝置列舉
⚲ API
- Shell 介面:ls /proc/bus/ /sys/bus/
另請參見 驅動模型的匯流排
參見 輸入:鍵盤、滑鼠等
PCI
⚲ Shell API
- lspci -vv
- column -t /proc/bus/pci/devices
主條目:PCI
USB
⚲ Shell API
- lsusb -v
- ls /sys/bus/usb/
- cat /proc/bus/usb/devices
⚙️ 內部結構
📖 參考
其他匯流排
🤖 嵌入式裝置的匯流排
- linux/gpio/driver.h 包含 linux/gpio.h 包含 drivers/gpio 原始碼 tools/gpio 原始碼
- drivers/i2c 原始碼 https://i2c.wiki.kernel.org
SPI
⚲ API
⚙️ 內部結構
📖 參考
硬體介面是任何作業系統的基本組成部分,它使處理器與計算機系統的其他硬體元件(記憶體、外設和匯流排、各種控制器)之間的通訊成為可能。
I/O 埠和暫存器是計算機系統中的電子元件,它們使 CPU 與其他電子控制器和裝置之間的通訊成為可能。
⚲ API
linux/regmap.h 包含 — 暫存器對映訪問 API
asm-generic/io.h 包含 — 通用 I/O 埠模擬。
- ioread32 識別符號 / iowrite32 識別符號 ...
- readl 識別符號/ writel 識別符號 ...
- 宏 {in,out}[bwl] 用於模擬 x86 風格的 PCI/ISA IO 空間
linux/ioport.h 包含 — 定義用於檢測、保留和分配系統資源的例程。
記憶體對映暫存器的函式
ioremapid ...
關鍵字:韌體、熱插拔、時鐘、複用器、引腳
⚙️ 內部結構
- drivers/acpisrc
- drivers/basesrc
- drivers/sdio 原始碼 - 安全數字輸入輸出
- drivers/virtiosrc
- drivers/hwmonsrc
- drivers/thermalsrc
- drivers/pinctrlsrc
- drivers/clksrc
📖 參考
- 引腳控制子系統 文件
- Linux 硬體監控 文件
- 韌體指南 文件
- 裝置樹 文件
- https://hwmon.wiki.kernel.org/
- LDD3:Linux 裝置模型
- http://www.tldp.org/LDP/tlk/dd/drivers.html
- http://www.xml.com/ldd/chapter/book/
- http://examples.oreilly.com/linuxdrive2/
核心是在兩個階段載入的 - 在第一個階段,核心(作為壓縮的映象檔案)被載入到記憶體並解壓縮,並且設定了一些基本功能,例如基本硬體和基本記憶體管理(記憶體分頁)。然後,控制最終切換到呼叫start_kernel 識別符號的主核心啟動過程,然後在生成單獨的空閒程序和排程器以及 init 程序(在使用者空間執行)之前執行大部分系統設定(中斷、記憶體管理的其餘部分、裝置和驅動程式初始化等)。
核心載入階段
載入的核心通常是映象檔案,使用 zlib 壓縮為 zImage 或 bzImage 格式。它頭部的例程執行最少的硬體設定,將映象完全解壓縮到高記憶體,並注意是否配置了任何 RAM 磁碟。然後,它透過 startup_64(對於 x86_64 架構)執行核心啟動。
- arch/x86/boot/compressed/vmlinux.lds.S 原始碼 - 連結器指令碼定義了入口點 startup_64 識別符號
- arch/x86/boot/compressed/head_64.S 原始碼 - 提取器的彙編
- extract_kernel 識別符號 - C 語言中的提取器
- 列印
Decompressing Linux... done. Booting the kernel.
核心啟動階段
核心的啟動函式(也稱為交換器或程序 0)建立記憶體管理(分頁表和記憶體分頁),檢測 CPU 型別和任何附加功能(例如浮點功能),然後透過呼叫 start_kernel 識別符號切換到非架構特定的 Linux 核心功能。
↯ 啟動呼叫層次結構
- arch/x86/kernel/vmlinux.lds.S src – 連結器指令碼
- arch/x86/kernel/head_64.S src – 未壓縮啟動程式碼的彙編
- arch/x86/kernel/head64.c src – 平臺相關的啟動
- init/main.c src – 主要初始化程式碼
- start_kernel id 200 SLOC
- mm_initid
- sched_initid
- rcu_init id – 讀-複製-更新
- rest_initid
- kernel_init id - 延遲核心執行緒 #1
- kernel_init_freeable id 此函式和以下函式都使用屬性 __init id 定義
- run_init_process id 顯然會執行第一個程序 man 1 init
- kthreadd id – 延遲核心執行緒 #2
- cpu_startup_entryid
- kernel_init id - 延遲核心執行緒 #1
- start_kernel id 200 SLOC
start_kernel id 執行了大量初始化函式。它設定了中斷處理(IRQs),進一步配置記憶體,啟動了 man 1 init 程序(第一個使用者空間程序),然後透過 cpu_startup_entry id 啟動了空閒任務。值得注意的是,核心啟動過程還會掛載之前載入的 初始 RAM 磁碟(initrd),作為啟動階段的臨時根檔案系統。initrd 允許驅動程式模組直接從記憶體載入,而無需依賴其他裝置(例如硬碟)及其訪問所需驅動程式(例如 SATA 驅動程式)。這種將部分驅動程式靜態編譯到核心中而將其他驅動程式從 initrd 載入的方式可以縮減核心體積。稍後透過呼叫 man 8 pivot_root / man 2 pivot_root 來切換根檔案系統,該呼叫會解除安裝臨時根檔案系統並將其替換為真正的根檔案系統,前提是真正的根檔案系統已可訪問。然後,臨時根檔案系統所使用的記憶體會被回收。
⚙️ 內部結構
...
[edit | edit source]📖 參考
- 關於 核心啟動 的文章
- 初始 RAM 磁碟 doc
- Linux 啟動過程
- init
- Linux (U)EFI 啟動過程
- 核心的命令列引數 doc
- 啟動配置 doc
- 啟動時記憶體管理 doc
- 核心啟動過程
- 核心初始化過程
📚 進一步閱讀
💾 歷史
- http://tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/
- http://www.tldp.org/LDP/lki/lki-1.html
- http://www.tldp.org/HOWTO/KernelAnalysis-HOWTO-4.html
掛起或重啟
[edit | edit source]🔧 待辦事項
⚲ API
⚙️ 內部結構
電源管理
[edit | edit source]關鍵字:掛起、警報、休眠。
⚲ API
- /sys/power/
- /sys/kernel/debug/wakeup_sources
- ⌨️動手操作
- sudo awk '{gsub("^ ","?")} NR>1 {if ($6) {print $1}}' /sys/kernel/debug/wakeup_sources
- linux/pm.hinc
- linux/pm_qos.hinc
- linux/pm_clock.hinc
- linux/pm_domain.hinc
- linux/pm_wakeirq.hinc
- linux/pm_wakeup.hinc
- linux/suspend.hinc
- pm_suspend id 掛起系統
- 掛起和喚醒依賴於
- man 2 timer_create 和 man 2 timerfd_create,如果使用時鐘 ID CLOCK_REALTIME_ALARM id 或 CLOCK_BOOTTIME_ALARM id,系統在掛起時會被喚醒。
- man 2 epoll_ctl 使用標誌 EPOLLWAKEUP id 會阻止掛起
- 另請參閱 man 7 capabilities CAP_WAKE_ALARM id、CAP_BLOCK_SUSPEND id
⚙️ 內部結構
- CONFIG_PMid
- CONFIG_SUSPENDid
- kernel/powersrc
- alarm_initid
- kernel/time/alarmtimer.csrc
- drivers/base/power src: wakeup_sources id
📖 參考
- PM 管理 doc
- CPU 和裝置 PM doc
- 電源管理 doc
- sysfs 電源測試 ABI doc
- https://lwn.net/Kernel/Index/#Power_management
- PowerTOP
- cpupower
- tlp – 應用筆記型電腦電源管理設定
- ACPI – 高階配置和電源介面
執行時 PM
[edit | edit source]關鍵字:執行時電源管理、裝置電源管理、機會性掛起、自動掛起、自動休眠。
⚲ API
- /sys/devices/.../power/
- async autosuspend_delay_ms control runtime_active_kids runtime_active_time runtime_enabled runtime_status runtime_suspended_time runtime_usage
- linux/pm_runtime.hinc
- SET_RUNTIME_PM_OPSid
👁 例子:ac97_pm id
⚙️ 內部結構
📖 參考