linux:内核如何定位并调用设备驱动初始化函数

作者:jafon.tian

转载请注明出处:https://blog.csdn.net/JT_Notes

写过linux驱动程序的人都知道需要将驱动的初始化函数通过module_init注册,然后在通过menuconfig配置的时候选择随内核一起编译(非模块),系统在启动的时候就能够自动调用驱动初始化函数了。真是一件神奇的事情!

驱动程序模板(模板来源:https://blog.csdn.net/zhuhuibeishadiao/article/details/51407438)

#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE ("GPL");//开源协议GPL 或者Dual BSD
MODULE_AUTHOR ("TOM");//作者
MODULE_DESCRIPTION ("MY_TEST");//描述此驱动
//EXPORT_NO_SYMBOLS;//不导出函数 可以不写
//EXPORT_SYMBOL(hello_data);//导出hello_data
int test_init(void)
{
   
    printk(KERN_INFO "hello world\n");
    return 0;
}
void test_exit(void)
{
   
   printk(KERN_INFO "goodbye world\n");
}
module_init(test_init); //注册DriverEntry	 
module_exit(test_exit); //注册DriverUnload

内核是如何知道存在这个驱动?又是在何时调用了驱动初始化函数?看来秘密应该就在这个module_init上。我们就从module_init入手,在linux kernel的源码中找寻这个问题的答案。下面是对相关代码的摘录,省略了不相关的部分。(文件位于include/linux/init.h)

/*
 * Used for initialization calls..
 */
typedef int (*initcall_t)(void);

/* initcalls are now grouped by functionality into separate 
 * subsections. Ordering inside the subsections is determined
 * by link order. 
 * For backwards compatibility, initcall() puts the call in 
 * the device init subsection.
 *
 * The `id' arg to __define_initcall() is needed so that multiple initcalls
 * can point at the same handler without causing duplicate-symbol build errors.
 */

#define __define_initcall(fn, id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn

#define device_initcall(fn)		__define_initcall(fn, 6)

#define __initcall(fn) device_initcall(fn)
/**
 * module_init() - driver initialization entry point
 * @x: function to be run at kernel boot time or module insertion
 * 
 * module_init() will either be called during do_initcalls() (if
 * builtin) or at module insertion time (if a module).  There can only
 * be one per module.
 */
#define module_init(x)	__initcall(x);

从代码我们可以看到module_init是一个嵌套宏定义,嵌套层次为:
module_init->__initcall->device_initcall->__define_initcall

经过这么多层嵌套,模板中的module_init(test_init)到底最后变成了什么模样?我们做个实验,首先对模板程序进行适当的调整,并命名为driver.c。

// driver.c
typedef int (*initcall_t)(void);
#define __define_initcall(fn, id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn

#define device_initcall(fn)		__define_initcall(fn, 6)

#define __initcall(fn) device_initcall(fn)

#define module_init(x)	__initcall(x);

int test_init(void)
{
   
    return 0;
}

module_init(test_init); 

接下在我们使用gcc对driver.c进行预处理

$ gcc -E driver.c 
# 1 "driver.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "driver.c"
typedef int (*initcall_t)(void);
# 12 "driver.c"
int test_init(void)
{
    return 0;
}

static initcall_t __initcall_test_init6 __used __attribute__((__s
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值