android底层驱动学习之 module_init的内核调用顺序

来源:互联网 发布:查看所有node版本号 编辑:程序博客网 时间:2024/06/13 04:57

1.一个驱动开始总有一 个init函数,那是怎么样实现的呢?-----基于将模块编译进内核方式的

可以看到在每个驱动都有module_init(要init的函数指针即函数名),在我focaltech_core.c是这样的:module_init(fts_ts_init);

2.追code可以看到以下流程:

#define module_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------------------------这个还不是很懂,先放放,有空再研究


最终我们看到的是module_init的真身,__define_initcall(level,fn,id),仔细推敲这个真身,知道这是个宏,它把传给module_init的函数名组装成以__initcall为前缀的、以6为后缀的函数名,并把这个函数定义到代码段.initcall6.init里面。


3.那.initcall6.init这个是什么鸟呢,发现在字符串的文件vmlinux.lds.h存在以下定义:

#define INITCALLS\
*(.initcallearly.init)\
VMLINUX_SYMBOL(__early_initcall_end) = .;\
  *(.initcall0.init)\
  *(.initcall0s.init)\
  *(.initcall1.init)\
  *(.initcall1s.init)\
  *(.initcall2.init)\
  *(.initcall2s.init)\
  *(.initcall3.init)\
  *(.initcall3s.init)\
  *(.initcall4.init)\
  *(.initcall4s.init)\
  *(.initcall5.init)\
  *(.initcall5s.init)\
*(.initcallrootfs.init)\
  *(
.initcall6.init)\
  *(.initcall6s.init)\
  *(.initcall7.init)\
  *(.initcall7s.init)


现在看到了.initcall6.init,这个东西相当于一个代码存储区,将驱动的init要执行的函数代码存在这里。

4.那接下来是不是应该来看看内核初始化流程,这样才能知道这段代码什么时候调用:

内核启动流程如下所示

在init/main.c里面,start_kernel----rest_init----- kernel_init---- do_basic_setup---- do_initcalls----有如下代码段:

static void __init do_initcalls(void)
{
initcall_t *fn;


for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
}

在上述代码中,__early_initcall_end,__initcall_end在INITCALLS内定义,见下,他们代表的是一些初始化函数的指针数组起始与结束地址,执行函数do_initcalls时,包含在这各指针数组里面的函数顺序的被调用以执行一些必要的初始化工作。至此,明白的各initcall的执行时刻了吧。

在vmlinux.lds.S里面

__initcall_start = .;

                     INITCALLS

              __initcall_end = .;

              __con_initcall_start = .;

                     *(.con_initcall.init)

              __con_initcall_end = .;

              __security_initcall_start = .;

                     *(.security_initcall.init)

              __security_initcall_end = .;

INITCALLS在__initcall_start = .与__initcall_end = .之间,表示INITCALLS宏内涵的相关段代码顺序存放在这里,module_init所代表段的镶嵌



5.以上是对于模块编译进内核的,如果通过insmod的方式呢?

insmod 是靠一个在kernel/module.c里定义的系统调用来实现的。

1. 此系统调用(sys_init_module )分配内核存储空间(kernel memory)给相关的模块,这个内存分配动作是由vmalloc完成;

2. 然后将该模块内容拷贝到这块存储空间里;

3. 接着声明内核引用该模块;

4. 呼叫该模块的初始化涵数

这样,一个插入模块的过程就完成了。

该函数准确应该是在 Linux/kernel/module.c里, 但是在里面你不会找到sys_init_module这个函数,因为这个函数是通过一个宏来实现的,

> /* This is where the real work happens */ 
> SYSCALL_DEFINE3(init_module, void __user *, umod, 
> unsigned long, len, const char __user *, uargs) 

这个宏就代表了sys_init_module这个函数。

请看SYSCALL_DEFINE3的定义有这么一句

if (mod->init != NULL)
ret =
do_one_initcall(mod->init);

是不是看到和上面类似的了,对,这个函数do_one_initcall在之前do_initcalls是有调用的。


至此应该明白了module_init的实现过程了。



0 0
原创粉丝点击