C语言 内核在哪里/如何加载静态链接的模块?



查看init/main.c#start_kernel,不清楚静态链接的内核模块在哪里加载,也不清楚内核如何获得它们的列表。

那么,在哪里加载了静态链接的内核模块

?

为了将一个模块编译为一个模块,将相应的Kconfig设置为m。如果设置为y,则所有模块代码都像所有其他"内置"代码一样编译。内核源文件和生成的目标文件被添加到所有其余的"内置"文件中。内核对象文件,然后将它们链接在一起以创建vmlinux映像。因此,当引导加载程序将这个整体映像加载到内存中以引导内核时,所有内置模块都将加载它。所需要的只是在初始化期间调用标记为module_init的函数。

至于这是如何发生的,简短的回答是,除非模块源文件被"作为模块"编译,否则module_init将给定的函数地址添加到函数地址数组中,然后在初始化期间调用该函数地址。


对于较长的答案,请考虑drivers/net/hamradio/mkiss.cmkiss模块。

Kconfig和编译器标志

Kconfig出现在drivers/net/hamradio/Kconfig:

config MKISS
tristate "Serial port KISS driver"
depends on AX25 && TTY
select CRC16
help
...
To compile this driver as a module, choose M here: the module
will be called mkiss.

选择结果是.config文件中的CONFIG_MKISS=yCONFIG_MKISS=m行。然后在drivers/net/hamradio/Makefile:

中使用
obj-$(CONFIG_MKISS)             += mkiss.o

因此,如果"builtin",则mkiss.o被添加到目标obj-y的列表中。如果"模块"它被添加到obj-m。经过一些路径处理后,obj-m变成scripts/Makefile.lib中的real-obj-m,然后用于确定编译器标志:

part-of-module = $(if $(filter $(basename $@).o, $(real-obj-m)),y)
modkern_cflags =                                          
$(if $(part-of-module),                           
$(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), 
$(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))
c_flags        = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     
-include $(srctree)/include/linux/compiler_types.h       
$(_c_flags) $(modkern_cflags)                           
$(basename_flags) $(modname_flags)

这里的要点是,我们得到$(KBUILD_CFLAGS_MODULE)模块,和$(KBUILD_CFLAGS_KERNEL)的内置。这些在顶级Makefile:

中定义
KBUILD_CFLAGS_KERNEL :=
KBUILD_CFLAGS_MODULE  := -DMODULE

内置函数初始化函数

MODULE预处理器宏定义的差异在include/linux/module.h中被用来提供module_init(和module_exit)的替代定义:

#ifndef MODULE
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module).  There can only
* be one per module.
*/
#define module_init(x)  __initcall(x);
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module.  If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x)  __exitcall(x);
#else /* MODULE */
...

跟踪initcall

所以,如果是内置的,module_init就变成了__initcall,和device_initcall一样,就像你在include/linux/init.h中看到的那样:

#define __initcall(fn) device_initcall(fn)

如果您可以遵循所有宏:

#define device_initcall(fn)             __define_initcall(fn, 6)
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
#define ___define_initcall(fn, id, __sec)                       
__unique_initcall(fn, id, __sec, __initcall_id(fn))
#define __unique_initcall(fn, id, __sec, __iid)                 
____define_initcall(fn,                                 
__initcall_stub(fn, __iid, id),                 
__initcall_name(initcall, __iid, id),           
__initcall_section(__sec, __iid))
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#define ____define_initcall(fn, __stub, __name, __sec)          
__define_initcall_stub(__stub, fn)                      
asm(".section   "" __sec "", "a"            n"     
__stringify(__name) ":                      n"     
".long      " __stringify(__stub) " - .     n"     
".previous                                  n");   
static_assert(__same_type(initcall_t, &fn));
#else
#define ____define_initcall(fn, __unused, __name, __sec)        
static initcall_t __name __used                         
__attribute__((__section__(__sec))) = fn;
#endif

或者如果你运行预处理器,你会得到这样的结果:

.section ".initcall6.init", "a"
__initcall__kmod_mkiss__502_979_mkiss_init_driver6:
.long mkiss_init_driver - .
.previous

mkiss_init_driver函数添加一个具有(相对)地址的唯一符号(对于给定函数的每次不同调用),位于.initcall6.init节中。"6";这里表示deviceinitcall订单。是:

0: pure
1: core
2: postcore
3: arch
4: subsys
5: fs
6: device
7: late

当所有这些目标文件链接在一起时,.initcallX.init节将被链接器合并,并且每个这样的节将是在初始化阶段调用的函数指针数组。这些数组的定义可以在include/asm-generic/vmlinux.lds.h:

中找到。
#define INIT_DATA_SECTION(initsetup_align)                              
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {               
INIT_DATA                                               
INIT_SETUP(initsetup_align)                             
INIT_CALLS                                              
CON_INITCALL                                            
INIT_RAM_FS                                             
}
#define INIT_CALLS                                                      
__initcall_start = .;                                   
KEEP(*(.initcallearly.init))                            
INIT_CALLS_LEVEL(0)                                     
INIT_CALLS_LEVEL(1)                                     
INIT_CALLS_LEVEL(2)                                     
INIT_CALLS_LEVEL(3)                                     
INIT_CALLS_LEVEL(4)                                     
INIT_CALLS_LEVEL(5)                                     
INIT_CALLS_LEVEL(rootfs)                                
INIT_CALLS_LEVEL(6)                                     
INIT_CALLS_LEVEL(7)                                     
__initcall_end = .;
#define INIT_CALLS_LEVEL(level)                                         
__initcall##level##_start = .;                          
KEEP(*(.initcall##level##.init))                        
KEEP(*(.initcall##level##s.init))

最后,您可以在init/main.c中看到这些数组的迭代:

static initcall_entry_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
...
static void __init do_initcalls(void)
{
...
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
...
do_initcall_level(level, command_line);
}
...
}
static void __init do_initcall_level(int level, char *command_line)
{
...
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(initcall_from_entry(fn));
}
int __init_or_module do_one_initcall(initcall_t fn)
{
...
ret = fn();
...
return ret;
}

相关内容

  • 没有找到相关文章