使用宏 *__init* 作为前缀的函数会发生什么?

What happens with functions using macro *__init* as prefix?

根据我在 Linux OS 中阅读的有关模块初始化的信息,我了解到具有 [=18 这样的前缀的函数调用=]__init 将被放置在单个 ELF 位置。

比如我们通常这样写:

int __init module_start()
{
....
}

以便在初始化结束后可以覆盖或删除特定的 ELF 位置(如果是这样的话)?如果我们想再次使用初始化,或者如果我想重新初始化任何模块,在知道初始化例程已被删除的情况下,采用什么方法?

__init 是一个 Linux 内核宏,它有点类似于普通的 gcc __attribute__(constructor),还有一个额外的特点,它也被解释为一个提示,代码可以是一旦它有 运行 就被释放(仅限模块,平台相关)。在 "normal" gcc/glibc C 程序中,libc 负责调用任何 init(和 fini)函数,在内核中您需要显式使用 module_init()(和 module_exit())。

来自 include/linux/init.h:

/* These macros are used to mark some functions or 
 * initialized data (doesn't apply to uninitialized data)
 * as `initialization' functions. The kernel can take this
 * as hint that the function is used only during the initialization
 * phase and free up used memory resources after
[...]

#define __init          __section(.init.text) __cold notrace

有类似的宏来标记退出函数和数据。这些是 compiler/linker 说明:

  • __section() 将代码放在 .init.text 部分(抛弃)
  • __cold 指示编译器(通过 __attribute__() 优化调用路径,因为该函数很少被调用(一次!)
  • notrace 以防止 ftrace
  • 可能出现的问题

重新调用模块初始化的一种简单方法是卸载并重新加载模块(有时对于使设备松开很有用)。

如果初始化例程的某些部分在 运行 时可能有用,则可以将它们放在标记为 __initnot 函数中,并且从真正的初始化中调用 功能。您可以用 __init 标记多个函数,但在 module_init() 中也必须只使用一个函数,否则您的代码不应调用此类函数。要点是模块初始化代码与设置内核 API 和初始化任何硬件或非硬件驱动程序功能一样重要(我猜这是你遇到的问题)。

另请参阅:

  • http://lwn.net/Kernel/LDD3/(免费 600 多页 PDF,略有过时但未过时)第 2 章中的 "hello world" 模块涵盖了您必须了解的基础知识
  • What does __init mean in the Linux kernel code?