驱动模块编写和驱动模块函数入口本质

驱动模块编写和驱动模块函数入口本质

#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

一步一步调用过程

posted @ 2023-11-01 21:12  _promise  阅读(32)  评论(0编辑  收藏  举报