符號名稱空間

以下文件描述瞭如何使用符號名稱空間來組織透過 EXPORT_SYMBOL() 宏系列匯出的核心內部符號的匯出介面。

簡介

引入符號名稱空間是為了組織核心內部 API 的匯出介面。它允許子系統維護者將其匯出的符號劃分到單獨的名稱空間中。這對於文件目的(例如 SUBSYSTEM_DEBUG 名稱空間)以及限制一組符號在核心其他部分的可用性非常有用。目前,使用匯出到名稱空間中的符號的模組,需要匯入該名稱空間。否則,核心將根據其配置,拒絕載入該模組或警告缺少匯入。

此外,還可以將符號放入模組名稱空間中,嚴格限制哪些模組可以使用這些符號。

如何定義符號名稱空間

符號可以使用不同的方法匯出到名稱空間中。所有這些方法都改變了 EXPORT_SYMBOL 及其相關宏生成 ksymtab 條目的方式。

使用 EXPORT_SYMBOL 宏

除了允許將核心符號匯出到核心符號表的 EXPORT_SYMBOL() 和 EXPORT_SYMBOL_GPL() 宏之外,還有用於將符號匯出到特定名稱空間的變體:EXPORT_SYMBOL_NS() 和 EXPORT_SYMBOL_NS_GPL()。它們接受一個額外的引數:作為字串常量的名稱空間。請注意,此字串不能包含空格。例如,要將符號 usb_stor_suspend 匯出到名稱空間 USB_STORAGE,請使用

EXPORT_SYMBOL_NS(usb_stor_suspend, "USB_STORAGE");

相應的 ksymtab 條目結構 kernel_symbol 將相應地設定成員 namespace。沒有匯出到名稱空間的符號將指向 NULL。如果未定義,則沒有預設名稱空間。modpost 和 kernel/module/main.c 分別在構建時或模組載入時使用名稱空間。

使用 DEFAULT_SYMBOL_NAMESPACE 定義

為一個子系統的所有符號定義名稱空間可能非常冗長且難以維護。因此,提供了一個預設定義(DEFAULT_SYMBOL_NAMESPACE),如果設定,它將成為所有未指定名稱空間的 EXPORT_SYMBOL() 和 EXPORT_SYMBOL_GPL() 宏擴充套件的預設值。

有多種方法可以指定此定義,具體取決於子系統和維護者的偏好選擇哪種方法。第一種選擇是在子系統的 Makefile 中定義預設名稱空間。例如,要將 usb-common 中定義的所有符號匯出到 USB_COMMON 名稱空間,請在 drivers/usb/common/Makefile 中新增如下一行

ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"USB_COMMON"'

這將影響所有 EXPORT_SYMBOL() 和 EXPORT_SYMBOL_GPL() 語句。當此定義存在時,使用 EXPORT_SYMBOL_NS() 匯出的符號仍將被匯出到作為名稱空間引數傳遞的名稱空間中,因為此引數優先於預設符號名稱空間。

定義預設名稱空間的第二種選擇是直接在編譯單元中作為預處理語句。上述示例將變為

#define DEFAULT_SYMBOL_NAMESPACE "USB_COMMON"

在相應的編譯單元中,位於 <linux/export.h> 的 #include 之前。通常它放在第一個 #include 語句之前。

使用 EXPORT_SYMBOL_GPL_FOR_MODULES() 宏

使用此宏匯出的符號被放入模組名稱空間。此名稱空間無法匯入。

此宏接受一個逗號分隔的模組名稱列表,只允許這些模組訪問此符號。支援簡單的尾部萬用字元。

例如

EXPORT_SYMBOL_GPL_FOR_MODULES(preempt_notifier_inc, "kvm,kvm-*")

將此符號的使用限制為名稱與給定模式匹配的模組。

如何使用名稱空間中匯出的符號

為了使用匯出到名稱空間中的符號,核心模組需要顯式匯入這些名稱空間。否則核心可能會拒絕載入該模組。模組程式碼需要使用 MODULE_IMPORT_NS 宏來匯入其使用的符號所在的名稱空間。例如,使用上述 usb_stor_suspend 符號的模組,需要使用如下語句匯入 USB_STORAGE 名稱空間

MODULE_IMPORT_NS("USB_STORAGE");

這將在模組中為每個匯入的名稱空間建立一個 modinfo 標籤。這帶來的副作用是,可以使用 modinfo 工具檢查模組匯入的名稱空間。

$ modinfo drivers/usb/storage/ums-karma.ko
[...]
import_ns:      USB_STORAGE
[...]

建議將 MODULE_IMPORT_NS() 語句新增到靠近其他模組元資料定義(如 MODULE_AUTHOR() 或 MODULE_LICENSE())的位置。

載入使用名稱空間符號的模組

在模組載入時(例如 insmod),核心將檢查模組引用的每個符號的可用性,以及其可能匯出到的名稱空間是否已被模組匯入。核心的預設行為是拒絕載入未指定足夠匯入的模組。將記錄一個錯誤,並且載入將因 EINVAL 失敗。為了允許載入不滿足此前提條件的模組,提供了一個配置選項:設定 MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y 將允許載入,但會發出警告。

自動建立 MODULE_IMPORT_NS 語句

在構建時可以很容易地檢測到缺少的名稱空間匯入。實際上,如果模組使用來自某個名稱空間的符號而未匯入該名稱空間,modpost 將發出警告。MODULE_IMPORT_NS() 語句通常會新增在特定位置(與其他模組元資料一起)。為了方便模組作者(和子系統維護者),提供了一個指令碼和 make 目標來修復缺少的匯入。可以透過以下方式修復缺少的匯入:

$ make nsdeps

模組作者的典型場景是

- write code that depends on a symbol from a not imported namespace
- ``make``
- notice the warning of modpost telling about a missing import
- run ``make nsdeps`` to add the import to the correct code location

對於引入名稱空間的子系統維護者,步驟非常相似。同樣,make nsdeps 最終將為樹內模組新增缺少的名稱空間匯入

- move or add symbols to a namespace (e.g. with EXPORT_SYMBOL_NS())
- ``make`` (preferably with an allmodconfig to cover all in-kernel
  modules)
- notice the warning of modpost telling about a missing import
- run ``make nsdeps`` to add the import to the correct code location

你也可以為外部模組構建執行 nsdeps。典型用法是

$ make -C <path_to_kernel_src> M=$PWD nsdeps

注意:它會很“樂意”地為模組名稱空間生成匯入語句;但這將不起作用,並導致構建和執行時失敗。