驱动模块编写和驱动模块函数入口本质
驱动模块编写和驱动模块函数入口本质
#include <linux/init.h>
#include <linux/module.h>
__init static int demo_init(void)
{
printk("hello world\n");
return 0;
}
__exit module_init(demo_init);
static void demo_exit(void)
{
printk("good bye world\n");
}
module_exit(demo_exit);
MODULE_AUTHOR("WANGC");
MODULE_VERSION("pluse");
MODULE_LICENSE("GPL");
//demo_init入口函数的查看
#define moudle_init(x) __initcall(x)
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
typedef int (*initcall_t)(void);
/*我们都知道一般typedef是用于取别名的。这里可以理解为 为一种返回值为int,参数为void的函数指针取了一个类型名为initcall_t,所以之后我们就可以直接用initcall_t去定义这种函数指针类型了。*/
define_initcall("6",demo_init,6)
static initcall_t __initcalldemo_init6 __used __attribute__((section(".initacall" 6 ".init"))) = demo_init
我们观察上面的宏,其中使用了attribute这个关键字,这个用法和我们在编写链接脚本的时的用法是一样的,他是用来定义我们将数据放在哪一个段,所以我们下来就来搜索一下他的链接脚本文件
527 .init.data : {
528 *(.init.data) *(.meminit.data) *(.init.rodata) . = ALIGN(8); __start_ftrace_events = .; *(_ftrace_events) __stop_ftrace_events = .; *(. meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .;
529 . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
530 __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.in itcall1.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) *(.initcall5 s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcal l6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
531 __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
532 __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
533 . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
534 }
_initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .;
它通过这两个标号,获得了这个段的开始地址和结束地址
之后我们再通过搜索找到使用这个标号的位置/init/main/
他将标号在c语言中用数组的性质在程序中体现出来
704 extern initcall_t __initcall_start[];
705 extern initcall_t __initcall0_start[];
706 extern initcall_t __initcall1_start[];
707 extern initcall_t __initcall2_start[];
708 extern initcall_t __initcall3_start[];
709 extern initcall_t __initcall4_start[];
710 extern initcall_t __initcall5_start[];
711 extern initcall_t __initcall6_start[];
712 extern initcall_t __initcall7_start[];
713 extern initcall_t __initcall_end[];
714
715 static initcall_t *initcall_levels[] __initdata = {
716 __initcall0_start,
717 __initcall1_start,
718 __initcall2_start,
719 __initcall3_start,
720 __initcall4_start,
721 __initcall5_start,
722 __initcall6_start,
723 __initcall7_start,
724 __initcall_end,
725 };
再搜索 initcall_levels 找到调用该段的函数
738 static void __init do_initcall_level(int level)
739 {
740 extern const struct kernel_param __start___param[], __stop___param[];
741 initcall_t *fn;
742
743 strcpy(static_command_line, saved_command_line);
744 parse_args(initcall_level_names[level],
745 static_command_line, __start___param,
746 __stop___param - __start___param,
747 level, level,
748 &repair_env_string);
749
750 --->重点 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) //这里
751 --->重点 do_one_initcall(*fn); //这里
752 }
//每一个段起始地址中的所有函数被调用
int __init_or_module do_one_initcall(initcall_t fn)
{
int count = preempt_count();
int ret;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn(); //在这里被调用
............
............
}
//所有初始化段里的函数被这个一一调用
754 static void __init do_initcalls(void)
755 {
756 int level;
757
758 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) //这里
759 do_initcall_level(level); //这里
760 }
module_init(fn) 要一个函数fn的地址 把这个变量放在特殊的段里 而那个段 在内核启动中自动变量这个段的起始地址和结束地址的里面每一个函数的地址,每个函数地址拿到以后去调用。
b start_kernel --> //汇编里面
start_kernel -->
kernel_thread(kernle_init) --> //内核开启一个线程
kernel_init-->
do_basic_setup-->
do_initcalls-->
do_initacall_level-->
do_one_initcall-->
fn
一步一步调用过程