Symbol Namespaces (符号命名空间)(翻译 by chatgpt)

发布时间 2023-12-07 23:04:43作者: 摩斯电码

原文:https://www.kernel.org/doc/html/latest/core-api/symbol-namespaces.html

以下文件描述了如何使用符号命名空间来构造通过EXPORT_SYMBOL()宏系列导出的内核符号的导出表面。

1. 介绍

符号命名空间被引入作为结构化内核API导出表面的一种手段。它允许子系统维护者将其导出的符号分成单独的命名空间。这对于文档目的(比如SUBSYSTEM_DEBUG命名空间)以及限制一组符号在内核其他部分的使用范围都很有用。截至目前,使用导出到命名空间的符号的模块需要导入该命名空间。否则,内核将根据其配置拒绝加载模块或警告缺少导入。

2. 如何定义符号命名空间

可以使用不同的方法将符号导出到命名空间。所有这些方法都会改变EXPORT_SYMBOL及其相关宏的工作方式,以创建ksymtab条目。

2.1 使用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将相应设置成员命名空间。未导出到命名空间的符号将引用NULL。如果未定义命名空间,则没有默认命名空间。modpost和kernel/module/main.c在构建时或模块加载时使用命名空间。

2.2 使用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()导出的符号仍将导出到作为命名空间参数传递的命名空间,因为此参数优先于默认符号命名空间。

第二种定义默认命名空间的选项是直接在编译单元中作为预处理语句进行定义。上述示例将如下编写:

#undef  DEFAULT_SYMBOL_NAMESPACE
#define DEFAULT_SYMBOL_NAMESPACE USB_COMMON

在任何使用EXPORT_SYMBOL宏之前的相应编译单元中。

3. 如何使用导出到命名空间的符号

为了使用导出到命名空间的符号,内核模块需要显式导入这些命名空间。否则,内核可能拒绝加载模块。模块代码需要使用宏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())附近。有关自动创建缺少的导入语句的方法,请参考第5节。

4. 加载使用命名空间符号的模块

在模块加载时(例如insmod),内核将检查模块中引用的每个符号的可用性以及模块是否已导入可能导出到的命名空间。内核的默认行为是拒绝加载未指定足够导入的模块。将记录错误并使用EINVAL失败加载。为了允许加载不满足此前提条件的模块,可以使用配置选项:设置MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y将启用加载,但会发出警告。

5. 自动创建MODULE_IMPORT_NS语句

在构建时可以轻松检测缺少的命名空间导入。实际上,如果模块使用了未导入的命名空间中的符号,modpost将发出警告。通常会在特定位置(与其他模块元数据一起)添加MODULE_IMPORT_NS()语句。为了使模块作者(和子系统维护者)的生活更轻松,提供了一个脚本和make目标来修复缺少的导入。可以使用以下命令修复缺少的导入:

$ make nsdeps

模块作者的典型场景可能是:

  • 编写依赖于未导入命名空间中的符号的代码
  • 运行make
  • 注意modpost关于缺少导入的警告
  • 运行make nsdeps将导入添加到正确的代码位置

对于引入命名空间的子系统维护者,步骤非常相似。同样,make nsdeps最终将为内核中的模块添加缺少的命名空间导入:

  • 移动或添加符号到命名空间(例如使用EXPORT_SYMBOL_NS())
  • 运行make(最好使用allmodconfig以覆盖所有内核模块)
  • 注意modpost关于缺少导入的警告
  • 运行make nsdeps将导入添加到正确的代码位置

也可以为外部模块构建运行nsdeps。典型用法是:

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