Linux驱动-module_init

Linux驱动开发中,设备是在加载linux内核的时候,通过 module_init 进行挂载。
这个module_init 是什么呢?这里网上搜索找了一些资料,学习记录一下。

一 Code实例

如下图,337 行的 module_init 会调用到 317行的ap3216c_init这个函数,ap3216c_init是驱动入口函数。
在这里,ap3216c_init( ) 会新增一个设备的驱动结构体——ap3216c_driver。
同样对应的,有加载就有卸载。338行的moudle_exit 会调用到330行的ap3216c_exit这个函数。
ap3216c_exit() 是驱动的出口函数,会去删除一个设备——ap3216c_driver。
在这里插入图片描述

二 module_init 架构图

在这里插入图片描述

三 module_init 调用流程

当bootloader加载完kernel并解压并放置与内存中准备开始运行,首先被调用的函数是start_kernel。

start_kernel -> reset_init -> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  
                               |  
                               |->static int __ref kernel_init(void *unused)  
                                     |  
                                     |-> kernel_init_freeable( )  
                                          |  
                                          |-> do_basic_setup();  
                                               |  
                                               |——> do_initcalls(); 

其中有个重要的流程:
在这里插入图片描述
fn = initcall_levels[level],假设level = 0,即fn = initcall_levels[0] =__initcall0_start;
那么 fn 指向了链接脚本中的 __initcall0_start地址。
每当 fn++ 也就是 fn 逐次指向注册到.initcall0.init 和 .initcall0s.init 段中的函数地址。
for 循环的条件是 fn< initcall_levels[ level +1 ] = initcall_levels[ 0 + 1 ] = initcall_levels[1]= __initcall1_start
简单来说,就是对所有的段,遍历每个段中的函数。
在这里插入图片描述

四 函数流程

从上到下的顺序执行。

总览

在这里插入图片描述

具体分析:

1 start_kernel()
在这里插入图片描述
2 rest_init(void)
在这里插入图片描述
3 kernel_init(void unused)
在这里插入图片描述
4 kernel_init_freeable
在这里插入图片描述
5 do_basic_setup
在这里插入图片描述
6 static void __init do_initcalls(void)
在这里插入图片描述
7 static void __init do_initcall_level (int level)
在这里插入图片描述
8 do_one_initcall(initcall_t fn)
在这里插入图片描述
9 arch/arm/kernel/vmlinux.lds
在这里插入图片描述
10 __define_initcall
在这里插入图片描述
11 include\linux\init.h
在这里插入图片描述在这里插入图片描述

五 为什么使用module_init

原因1:使用方便
在传统的思想中,各个模块的初始化函数会在一个固定的init函数里调用比如:
一般会定义一个总的init 函数,在这个函数里面实现各个模块的init。
当要新增一个模块的时候,就需要对这个init函数进行修改。

void init()
{
    init_a();
    init_b();
    ...
}

使用module_init,即可直接新增模块而不用去修改这个所谓的“init”函数。

原因2:初始化之后可以释放代码空间。
使用moudle_init,初始化函数在完成初始化后,代码占用的空间会被释放。
linux kernel中有很大一部分代码是设备驱动代码,这些驱动代码都有初始化和反初始化函数,这些代码一般都只执行一次,为了有更有效的利用内存,这些代码所占用的内存可以释放出来。
linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性。在kernel初始化后期,释放所有这些函数代码所占的内存空间。

实现方式:
连接器把带__init属性的函数放在同一个section里,在用完以后,把整个section释放掉。

init/main.c中start_kernel,这个函数的最后一行是rest_init()
在这里插入图片描述

六 参考

https://blog.csdn.net/weixin_44698673/article/details/123904354
https://blog.csdn.net/u013216061/article/details/72511653
https://blog.csdn.net/yxtxiaotian/article/details/83985965

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值