一,linux模块程序示例
下面是一个linux模块的示例程序:
- #include <linux/init.h>
- #include <linux/module.h>
- MODULE_LICENSE(”Dual BSD/GPL”);
- MODULE_AUTHOR(”Radia”);
- static int hello_init()
- {
- printk(KERN_EMERG ”hello module has been mount!\n”);
- return 0;
- }
- static void hello_exit()
- {
- printk(KERN_EMERG ”hello module has been remove!\n”);
- }
- module_init(hello_init);
- module_exit(hello_exit);
#include <linux/init.h>
include <linux/module.h>
MODULE_LICENSE(“Dual BSD/GPL”);
MODULE_AUTHOR(“Radia”);
static int hello_init()
{
printk(KERN_EMERG “hello module has been mount!\n”);
return 0;
}
static void hello_exit()
{
printk(KERN_EMERG “hello module has been remove!\n”);
}
module_init(hello_init);
module_exit(hello_exit); 二,module_init函数定义
查看其中module_init()的定义,发现在kernel\include\linux\init.h
- #define module_init(x) __initcall(x);
#define module_init(x) __initcall(x);而__initcall(x)定义在kernel\include\linux\init.h
- #define pure_initcall(fn) __define_initcall(fn, 0)
- #define core_initcall(fn) __define_initcall(fn, 1)
- #define core_initcall_sync(fn) __define_initcall(fn, 1s)
- #define postcore_initcall(fn) __define_initcall(fn, 2)
- #define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
- #define arch_initcall(fn) __define_initcall(fn, 3)
- #define arch_initcall_sync(fn) __define_initcall(fn, 3s)
- #define subsys_initcall(fn) __define_initcall(fn, 4)
- #define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
- #define fs_initcall(fn) __define_initcall(fn, 5)
- #define fs_initcall_sync(fn) __define_initcall(fn, 5s)
- #define rootfs_initcall(fn) __define_initcall(fn, rootfs)
- #define device_initcall(fn) __define_initcall(fn, 6)
- #define device_initcall_sync(fn) __define_initcall(fn, 6s)
- #define late_initcall(fn) __define_initcall(fn, 7)
- #define late_initcall_sync(fn) __define_initcall(fn, 7s)
- #define __initcall(fn) device_initcall(fn)
#define pure_initcall(fn) __define_initcall(fn, 0)
define core_initcall(fn) __define_initcall(fn, 1)
define core_initcall_sync(fn) __define_initcall(fn, 1s)
define postcore_initcall(fn) __define_initcall(fn, 2)
define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
define arch_initcall(fn) __define_initcall(fn, 3)
define arch_initcall_sync(fn) __define_initcall(fn, 3s)
define subsys_initcall(fn) __define_initcall(fn, 4)
define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
define fs_initcall(fn) __define_initcall(fn, 5)
define fs_initcall_sync(fn) __define_initcall(fn, 5s)
define rootfs_initcall(fn) __define_initcall(fn, rootfs)
define device_initcall(fn) __define_initcall(fn, 6)
define device_initcall_sync(fn) __define_initcall(fn, 6s)
define late_initcall(fn) __define_initcall(fn, 7)
define late_initcall_sync(fn) __define_initcall(fn, 7s)
define __initcall(fn) device_initcall(fn)其中我们发现了一系列的入口函数,这些函数其实都是对__define_initcall的封装。__define_initcall中的第二个参数是调用顺序,数字越小在linux启动过程中调用的越早。可以看到我们熟悉的模块入口函数device_initcall的优先级是6,subsys_initcall的优先级是4.
三,__define_initcall
我们再深入一步看看define_initcall的实现,实现部分也在kernel\include\linux\init.h中
- #define _define_initcall(fn, id) \
- static initcall_t __initcall##fn##id _used \
- __attribute((section(”.initcall” #id “.init”))) = fn; \
- LTO_REFERENCE_INITCALL(__initcall##fn##id)
#define __define_initcall(fn, id) \这个宏定义是将我们定义的模块初始化函数按照初始化优先级放到相应的.initcallX.init段中,如subsys_initcall定义的函数放到.initcall4.init段中。
static initcall_t _initcall##fn##id __used \
attribute((section(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(_initcall##fn##id)
详细解释一下这个宏的定义:
1,__define_initcall(fn, id)
这个宏有两个参数fn、id
2,static initcall_t _initcall##fn##id __used
定义了一个initcall_t类型的函数指针。
initcall_t的定义为typedef int (*initcall_t)(void);,是一个参数为空,返回值为int的函数指针类型。如我们第一个示例程序中module_init(hello_init); 传递给module_init 的函数就是这个类型的static int hello_init();
这个新定义的函数指针名称是有fn和id两个参数组成的_initcall##fn##id
__used告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。
3,attribute((section(“.initcall” #id “.init”))) = fn;
attribute((section(“”)))是为链接器指定链接的section,这里按照id将各个初始化函数放置到不同的.initcallX.init段中。
然后将初始化函数指针赋值给了新定义的函数指针。
4,LTO_REFERENCE_INITCALL(_initcall##fn##id)
是一个关于gcc编译器优化的宏。
四,启动段.initcallX.init
查看KERNEL_OBJ\arch\arm\kernel\vmlinux.lds发现lds中定义的各个section的具体内容如下
- (.init.data) (.meminit.data) (.init.rodata) . = ALIGN(8); __start_ftrace_events = .; (_ftrace_events) __stop_ftrace_events = .; . = ALIGN(8); __start_kprobe_blacklist = .; (_kprobe_blacklist) __stop_kprobe_blacklist = .; (.meminit.rodata) . = ALIGN(8); __reservedmem_of_table = .; (__reservedmem_of_table) (__reservedmem_of_table_end) . = ALIGN(8); __clksrc_of_table = .; (__clksrc_of_table) (__clksrc_of_table_end) . = ALIGN(8); __iommu_of_table = .; (__iommu_of_table) (__iommu_of_table_end) . = ALIGN(8); __cpu_method_of_table = .; (__cpu_method_of_table) (__cpu_method_of_table_end) . = ALIGN(32); __dtb_start = .; (.dtb.init.rodata) __dtb_end = .; . = ALIGN(8); __irqchip_of_table = .; (__irqchip_of_table) (__irqchip_of_table_end) . = ALIGN(8); __earlycon_of_table = .; (__earlycon_of_table) (__earlycon_of_table_end)
- . = ALIGN(16); __setup_start = .; (.init.setup) __setup_end = .;
- __initcall_start = .; (.initcallearly.init) __initcall0_start = .; (.initcall0.init) (.initcall0s.init) __initcall1_start = .; (.initcall1.init) (.initcall1s.init) __initcall2_start = .; (.initcall2.init) (.initcall2s.init) __initcall3_start = .; (.initcall3.init) (.initcall3s.init) __initcall4_start = .; (.initcall4.init) (.initcall4s.init) __initcall5_start = .; (.initcall5.init) (.initcall5s.init) __initcallrootfs_start = .; (.initcallrootfs.init) (.initcallrootfss.init) __initcall6_start = .; (.initcall6.init) (.initcall6s.init) __initcall7_start = .; (.initcall7.init) (.initcall7s.init) __initcall_end = .;
- __con_initcall_start = .; (.con_initcall.init) __con_initcall_end = .;
(.init.data) (.meminit.data) (.init.rodata) . = ALIGN(8); __start_ftrace_events = .; (_ftrace_events) __stop_ftrace_events = .; . = ALIGN(8); __start_kprobe_blacklist = .; (_kprobe_blacklist) __stop_kprobe_blacklist = .; (.meminit.rodata) . = ALIGN(8); __reservedmem_of_table = .; (__reservedmem_of_table) (__reservedmem_of_table_end) . = ALIGN(8); __clksrc_of_table = .; (__clksrc_of_table) (__clksrc_of_table_end) . = ALIGN(8); __iommu_of_table = .; (__iommu_of_table) (__iommu_of_table_end) . = ALIGN(8); __cpu_method_of_table = .; (__cpu_method_of_table) (__cpu_method_of_table_end) . = ALIGN(32); __dtb_start = .; (.dtb.init.rodata) __dtb_end = .; . = ALIGN(8); __irqchip_of_table = .; (__irqchip_of_table) (__irqchip_of_table_end) . = ALIGN(8); __earlycon_of_table = .; (__earlycon_of_table) *(__earlycon_of_table_end)我们上面定义的函数都落在了__initcall_start起始地址和__con_initcall_end结束地址之间。其中的section划分与init.h定义的相同。
. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
__initcall_start = .; (.initcallearly.init) __initcall0_start = .; (.initcall0.init) (.initcall0s.init) __initcall1_start = .; (.initcall1.init) (.initcall1s.init) __initcall2_start = .; (.initcall2.init) (.initcall2s.init) __initcall3_start = .; (.initcall3.init) (.initcall3s.init) __initcall4_start = .; (.initcall4.init) (.initcall4s.init) __initcall5_start = .; (.initcall5.init) (.initcall5s.init) __initcallrootfs_start = .; (.initcallrootfs.init) (.initcallrootfss.init) __initcall6_start = .; (.initcall6.init) (.initcall6s.init) __initcall7_start = .; (.initcall7.init) *(.initcall7s.init) __initcall_end = .;
__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
五,do_initcalls
这些入口函数都是由do_initcalls函数来调用的
kernel\init\main.c
- static void __init do_initcalls(void)
- {
- int level;
- for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
- do_initcall_level(level);
- }
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}do_initcall_level定义如下
- static void __init do_initcall_level(int level)
- {
- initcall_t *fn;
- strcpy(initcall_command_line, saved_command_line);
- parse_args(initcall_level_names[level],
- initcall_command_line, __start___param,
- __stop___param - __start___param,
- level, level,
- NULL, &repair_env_string);
- for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
- do_one_initcall(*fn);
- }
static void __init do_initcall_level(int level)
{
initcall_t *fn;
strcpy(initcall_command_line, saved_command_line);
parse_args(initcall_level_names[level],
initcall_command_line, __start___param,
__stop___param - __start___param,
level, level,
NULL, &repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
}initcall_levels数组定义如下:
- extern initcall_t __initcall_start[];
- extern initcall_t __initcall0_start[];
- extern initcall_t __initcall1_start[];
- extern initcall_t __initcall2_start[];
- extern initcall_t __initcall3_start[];
- extern initcall_t __initcall4_start[];
- extern initcall_t __initcall5_start[];
- extern initcall_t __initcall6_start[];
- extern initcall_t __initcall7_start[];
- extern initcall_t __initcall_end[];
- static initcall_t *initcall_levels[] __initdata = {
- __initcall0_start,
- __initcall1_start,
- __initcall2_start,
- __initcall3_start,
- __initcall4_start,
- __initcall5_start,
- __initcall6_start,
- __initcall7_start,
- __initcall_end,
- };
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};其中包含了上面定义的各个段的其实地址。
do_one_initcall执行一个优先级的所有函数,定义如下
- int __init_or_module do_one_initcall(initcall_t fn)
- {
- int count = preempt_count();
- int ret;
- char msgbuf[64];
- if (initcall_blacklisted(fn))
- return -EPERM;
- if (initcall_debug)
- ret = do_one_initcall_debug(fn);
- else
- ret = fn();
- msgbuf[0] = 0;
- if (preempt_count() != count) {
- sprintf(msgbuf, ”preemption imbalance ”);
- preempt_count_set(count);
- }
- if (irqs_disabled()) {
- strlcat(msgbuf, ”disabled interrupts ”, sizeof(msgbuf));
- local_irq_enable();
- }
- WARN(msgbuf[0], “initcall %pF returned with %s\n”, fn, msgbuf);
- return ret;
- }
int __init_or_module do_one_initcall(initcall_t fn)
{
int count = preempt_count();
int ret;
char msgbuf[64];
if (initcall_blacklisted(fn))
return -EPERM;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn();
msgbuf[0] = 0;
if (preempt_count() != count) {
sprintf(msgbuf, "preemption imbalance ");
preempt_count_set(count);
}
if (irqs_disabled()) {
strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
local_irq_enable();
}
WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
return ret;
}其中initcall_blacklisted会遍历所有函数并执行。
- static bool __init_or_module initcall_blacklisted(initcall_t fn)
- {
- struct list_head *tmp;
- struct blacklist_entry *entry;
- charchar *fn_name;
- fn_name = kasprintf(GFP_KERNEL, ”%pf”, fn);
- if (!fn_name)
- return false;
- list_for_each(tmp, &blacklisted_initcalls) {
- entry = list_entry(tmp, struct blacklist_entry, next);
- if (!strcmp(fn_name, entry->buf)) {
- pr_debug(”initcall %s blacklisted\n”, fn_name);
- kfree(fn_name);
- return true;
- }
- }
- kfree(fn_name);
- return false;
- }
static bool __init_or_module initcall_blacklisted(initcall_t fn)
{
struct list_head *tmp;
struct blacklist_entry *entry;
char *fn_name;
fn_name = kasprintf(GFP_KERNEL, "%pf", fn);
if (!fn_name)
return false;
list_for_each(tmp, &blacklisted_initcalls) {
entry = list_entry(tmp, struct blacklist_entry, next);
if (!strcmp(fn_name, entry->buf)) {
pr_debug("initcall %s blacklisted\n", fn_name);
kfree(fn_name);
return true;
}
}
kfree(fn_name);
return false;
}
一,linux模块程序示例
下面是一个linux模块的示例程序:
- #include <linux/init.h>
- #include <linux/module.h>
- MODULE_LICENSE(”Dual BSD/GPL”);
- MODULE_AUTHOR(”Radia”);
- static int hello_init()
- {
- printk(KERN_EMERG ”hello module has been mount!\n”);
- return 0;
- }
- static void hello_exit()
- {
- printk(KERN_EMERG ”hello module has been remove!\n”);
- }
- module_init(hello_init);
- module_exit(hello_exit);
#include <linux/init.h>