linux内核驱动 之 module_init解析 (上)

来源:互联网 发布:土建计价软件 编辑:程序博客网 时间:2024/05/22 01:58

linux内核驱动 之 module_init解析 (上)

转载 2017年01月23日 13:55:54
  • 426
  • 编辑
  • 删除

linux内核驱动 之 module_init解析 (上)

转自:

http://blog.csdn.net/richard_liujh/article/details/45669207


写过linux驱动的程序猿都知道module_init() 这个函数。那么我们来了解一下module_init这个函数的具体功能和执行过程

在kernel源码目录中找到include\linux\init.h文件

[cpp] view plain copy
print?
  1. <span style=“font-family:SimSun;font-size:14px;”>/** 
  2.  * module_init() - driver initialization entry point 
  3.  * @x: function to be run at kernel boot time or module insertion 
  4.  *  
  5.  * module_init() will either be called during do_initcalls() (if 
  6.  * builtin) or at module insertion time (if a module).  There can only 
  7.  * be one per module. 
  8.  */  
  9. #define module_init(x)  __initcall(x);</span>  
这里面就有对module_init 的定义,我们发现

module_init(x)是一个宏定义,那么_initcall(x)又是什么呢?

[cpp] view plain copy
print?
  1. #define __initcall(fn) device_initcall(fn)  
感觉怪怪的,怎么这么多的宏??再解释这个之前,我们再来看看更多的宏定义吧哭


完整的宏定义如下:

__define_initcall:

[cpp] view plain copy
print?
  1. /* initcalls are now grouped by functionality into separate  
  2.  * subsections. Ordering inside the subsections is determined 
  3.  * by link order.  
  4.  * For backwards compatibility, initcall() puts the call in  
  5.  * the device init subsection. 
  6.  * 
  7.  * The `id’ arg to __define_initcall() is needed so that multiple initcalls 
  8.  * can point at the same handler without causing duplicate-symbol build errors. 
  9.  */  
  10.   
  11. #define __define_initcall(fn, id) \  
  12.     static initcall_t __initcall_##fn##id __used \  
  13.     __attribute__((__section__(”.initcall” #id “.init”))) = fn  

initcalls:

[cpp] view plain copy
print?
  1. </pre><pre name=“code” class=“cpp”>/  
  2.  * A ”pure” initcall has no dependencies on anything else, and purely  
  3.  * initializes variables that couldn’t be statically initialized.  
  4.  *  
  5.  * This only exists for built-in code, not for modules.  
  6.  * Keep main.c:initcall_level_names[] in sync.  
  7.  */  
  8. #define pure_initcall(fn)       __define_initcall(fn, 0)  
  9.   
  10. #define core_initcall(fn)       __define_initcall(fn, 1)  
  11. #define core_initcall_sync(fn)      __define_initcall(fn, 1s)  
  12. #define postcore_initcall(fn)       __define_initcall(fn, 2)  
  13. #define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)  
  14. #define arch_initcall(fn)       __define_initcall(fn, 3)  
  15. #define arch_initcall_sync(fn)      __define_initcall(fn, 3s)  
  16. #define subsys_initcall(fn)     __define_initcall(fn, 4)  
  17. #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)  
  18. #define fs_initcall(fn)         __define_initcall(fn, 5)  
  19. #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)  
  20. #define rootfs_initcall(fn)     __define_initcall(fn, rootfs)  
  21. #define device_initcall(fn)     __define_initcall(fn, 6)  
  22. #define device_initcall_sync(fn)    __define_initcall(fn, 6s)  
  23. #define late_initcall(fn)       __define_initcall(fn, 7)  
  24. #define late_initcall_sync(fn)      __define_initcall(fn, 7s)  
  25.   
  26. #define __initcall(fn) device_initcall(fn)  

Note:下面用xxx_initcall来代表pure_initcall,core_initcall、core_initcall_sync … …

我们可以看到非常多的xxx_initcall宏函数定义,他们都是通过__define_initcall 实现的。在__define_initcall里面包含了两个参数,一个是fn,另一个则是id。那么,这么多的宏又有何用??

我们来到init\main.c文件中可以找到函数do_initcalls

[cpp] view plain copy
print?
  1. static void __init do_initcalls(void)  
  2. {  
  3.     int level;  
  4.   
  5.     for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)  
  6.         do_initcall_level(level);  
  7. }  
很明显do_initcalls中有一个for循环,那么此循环就是按照优先级顺序执行一些函数的。那么问题又来了,执行哪些函数??我们看看do_initcalls这个名字。是不是initcall非常的眼熟?没错就是上面我们提到过的宏定义xxx_initcall里面就有initcall。

所以,我们先来解释一下这些宏有什么用
还是从我们最熟悉的地方module_init(fn)开始说起,其中fn是module_init的参数,fn是一个函数指针(函数名)。

module_init(fn)—> __initcall(fn) —> device_initcall(fn) —> __define_initcall(fn, 6)

所以当我们写module_init(fn)最终我们可以简化成以下内容(假设module_init的参数为test_init)

module_init(test_init) —> __define_initcall(test_init, 6)

[cpp] view plain copy
print?
  1. #define __define_initcall(fn, id) \  
  2.     static initcall_t __initcall_##fn##id __used \  
  3.     __attribute__((__section__(”.initcall” #id “.init”))) = fn  

简单补充:

符号作用举例
##
“##”符号可以
是连接的意思
例如 __initcall_##fn##id 为__initcall_fnid
那么,fn = test_init,id = 6时,__initcall_##fn##id 为 __initcall_test_init6
#
“#”符号可以
是字符串化的意思
例如
#id 为 “id”,id=6 时,#id“6”


通过上面的定义,我们把module_init(test_init)给替换如下内容

static initcall_t __initcall_test6 __used __attribute__((__section__(“.initcall”“6” “.init”))) =test_init

是不是看起来更加头疼惊恐,那么我们说简单一点。通过__attribute__(__section__)设置函数属性,也就是将test_init放在.initcall6.init段中。这个段在哪用?这就要涉及到链接脚本了。

大家可以到kernel目录arch中,根据自己的处理器平台找到对应的链接脚本。例如我现在的平台是君正m200(mips架构),可能大部分是arm架构。

在arch/mips/kernel/vmlinux.lds这个链接脚本里面有如下一段代码

[python] view plain copy
print?
  1. __init_begin = .;  
  2.  . = ALIGN(4096); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text) *(.cpuinit.text) *(.meminit.text) _einittext = .; }  
  3.  .init.data : AT(ADDR(.init.data) - 0) { *(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.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) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .; __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .; . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info) }  
  4.  . = ALIGN(4);  
当然,关于链接脚本又有很多多动要讲。所以现在我们不关心里面的具体含义,我们可以观察到上面有这些字符串使我们比较熟悉的:__initcall6_start = .; *(.initcall6.init) *(.initcall6s.init)。链接脚本里的东西看似很乱很难,其实是非常有逻辑有规律可循的,我们来简单解释下面一行的代码作用

[cpp] view plain copy
print?
  1. __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init)   
其中__initcall6_start是一个符号,链接器用到的。__initcall6_start = .; ,其中的 ‘.’符号是对当前地址的一个引用,也就说把当前的地址给了符号__initcall6_start, *(.initcall6.init) *(.initcall6s.init) 的意思是所有的.initcall6.init段和.initcall6s.init段的内容从__initcall6_start为起始地址开始链接。

.initcall0.init .initcall0s.init .initcall1.init .initcall1s.init …… .initcall7.init .initcall7s.init

上面的内容都出现在了链接脚本中,而0,0s,1,1s,2,2s …… 6,6s,7,7s 有没有觉得在哪里见过? 我们回顾一下initcalls里面的定义

[cpp] view plain copy
print?
  1. #define pure_initcall(fn)       __define_initcall(fn, 0)  
  2.   
  3. #define core_initcall(fn)       __define_initcall(fn, 1)  
  4. #define core_initcall_sync(fn)      __define_initcall(fn, 1s)  
  5. #define postcore_initcall(fn)       __define_initcall(fn, 2)  
  6. #define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)  
  7. #define arch_initcall(fn)       __define_initcall(fn, 3)  
  8. #define arch_initcall_sync(fn)      __define_initcall(fn, 3s)  
  9. #define subsys_initcall(fn)     __define_initcall(fn, 4)  
  10. #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)  
  11. #define fs_initcall(fn)         __define_initcall(fn, 5)  
  12. #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)  
  13. #define rootfs_initcall(fn)     __define_initcall(fn, rootfs)  
  14. #define device_initcall(fn)     __define_initcall(fn, 6)  
  15. #define device_initcall_sync(fn)    __define_initcall(fn, 6s)  
  16. #define late_initcall(fn)       __define_initcall(fn, 7)  
  17. #define late_initcall_sync(fn)      __define_initcall(fn, 7s)  
这里面就有0,0s,1,1s,2,2s …… 6,6s,7,7s,也就是__define_initcall(fn, id)中的第二个参数 id。很显然这个id的值不是我们在调用module_init的时候传过去的。数字id 0~7代表的是不同的优先级(0最高,module_init对应的优先级为6,所以一般我们注册的驱动程序优先级为6),链接脚本里面根据我们注册不同的id,将我们的函数fn放入对应的地址里面。根据上面的分析,test_init放在.initcall6.init段中。

在kernel启动过程中,会调用do_initcalls函数一次调用我们通过xxx_initcall注册的各种函数,优先级高的先执行。所以我们通过module_init注册的函数在kernel启动的时候会被顺序执行。

由于时间原因,只能把具体执行过程放在linux内核很吊之 module_init解析 (下)再分析了。

阅读全文
#include #include #include …
  • dysh1985
  • dysh1985
  • 2012-05-24 09:46
  • 14338

Kernel启动时 驱动是如何加载的module_init,加载的次序如何;略见本文

Init.h中有相关initcall的启动次序,在system.map中可看出具体的__initcall指针的前后次序 
#define pure_initcall(fn) __define_i…
  • lijiuliang
  • lijiuliang
  • 2012-10-13 23:31
  • 4329

Linux内核很吊之 module_init解析 (下)

转自: http://blog.csdn.net/richard_liujh/article/details/46758073忙了一段时间,终于有时间把inux内核很吊之 module_ini…
  • UranusC
  • UranusC
  • 2017-01-23 13:58
  • 95

一步步将vim改造成C/C++开发环境(IDE)

我的vim IDE界面: 1、安装Vim和Vim基本插件首先安装好Vim和Vim的基本插件。这些使用apt-get安装即可:lingd@ubuntu:~/arm$sudo apt-…
  • yinjiabin
  • yinjiabin
  • 2012-12-04 10:21
  • 7850

一步一步的打造好用的Vim(图解)

一步一步的打造好用的Vim(图解)作者:草帽的后花园——小懒虫        转载请注明:草帽的后花园        首先,创建这个文档,是一直都有的想法,有两个原因,一个是为了自己保存,…
  • backgarden_straw
  • backgarden_straw
  • 2012-09-12 16:06
  • 1108

Linux内核很吊之 module_init解析 (下)

分析module_init 的作用。和module_init注册函数过程,以及注册函数被执行的过程。
  • Richard_LiuJH
  • Richard_LiuJH
  • 2015-07-04 22:00
  • 1998

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个…
  • yuyin86
  • yuyin86
  • 2012-03-14 08:51
  • 342

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个…
  • cuijianzhongswust
  • cuijianzhongswust
  • 2011-11-25 09:12
  • 603

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个…
  • lionfire
  • lionfire
  • 2011-07-28 17:41
  • 831

LINUX内核驱动第三版

  • 2013-11-20 16:06
  • 986KB
  • 下载

[zip文件] 国嵌—嵌入式LINUX内核驱动深入班

  • 2014-04-13 11:56
  • 27.65MB
  • 下载

Linux内核模块分析(module_init宏)

我们在学习Linux驱动开发时,首先需要了解Linux的模块化机制(module),但是module并不仅仅用于支撑驱动的加载和卸载。一个最简单的模块例子如下:// filename: HelloWo…
  • luckydarcy
  • luckydarcy
  • 2016-05-17 03:25
  • 674

嵌入式LINUX内核驱动进阶班实验手册_实验1.1

  • 2011-03-12 12:07
  • 6.00MB
  • 下载

Linux内核驱动模块编程指南

  • 2008-09-20 11:58
  • 50KB
  • 下载

linux内核段属性机制(以subsys_initcall和module_init为例)

linux内核段属性机制以subsys_initcall和module_init为例
  • TongxinV
  • TongxinV
  • 2017-01-27 11:04
  • 614
<iframe width="300" height="250" scrolling="no" src="//pos.baidu.com/s?hei=250&amp;wid=300&amp;di=u3032528&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Furanusc%2Farticle%2Fdetails%2F54693121&amp;chi=3&amp;exps=111000&amp;dtm=HTML_POST&amp;tpr=1507982473487&amp;ant=0&amp;tlm=1507982473&amp;col=zh-CN&amp;cfv=0&amp;cja=false&amp;drs=3&amp;par=1366x768&amp;cpl=15&amp;psr=1366x768&amp;tcn=1507982473&amp;dai=1&amp;pss=1287x3336&amp;pis=-1x-1&amp;dc=2&amp;cec=UTF-8&amp;cdo=-1&amp;ari=2&amp;ti=linux%E5%86%85%E6%A0%B8%E9%A9%B1%E5%8A%A8%20%E4%B9%8B%20module_init%E8%A7%A3%E6%9E%90%20%EF%BC%88%E4%B8%8A%EF%BC%89%20-%20CSDN%E5%8D%9A%E5%AE%A2&amp;ps=456x910&amp;dis=0&amp;ccd=24&amp;dri=1&amp;ltr=http%3A%2F%2Fblog.csdn.net%2Furanusc%3Fviewmode%3Dlist&amp;pcs=1287x680&amp;cce=true&amp;cmi=30"></iframe>

在线课程

  • Presto服务治理与架构优化在京东的实践应用

    Presto服务治理与架构优化在京东的实践应用

    讲师:王哲涵

  • 【免费直播】Python最佳学习路线

    【免费直播】Python最佳学习路线

    讲师:韦玮

<iframe width="300" height="250" scrolling="no" src="http://pos.baidu.com/s?hei=250&amp;wid=300&amp;di=u3032528&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Furanusc%2Farticle%2Fdetails%2F54693121&amp;dc=2&amp;dai=2&amp;tcn=1507982473&amp;cfv=0&amp;cmi=30&amp;col=zh-CN&amp;exps=111000&amp;par=1366x768&amp;psr=1366x768&amp;pis=-1x-1&amp;tpr=1507982473487&amp;ltr=http%3A%2F%2Fblog.csdn.net%2Furanusc%3Fviewmode%3Dlist&amp;tlm=1507982473&amp;cec=UTF-8&amp;ccd=24&amp;dis=0&amp;ari=2&amp;drs=3&amp;ps=956x910&amp;ant=0&amp;cce=true&amp;cpl=15&amp;pcs=1287x680&amp;pss=1287x3336&amp;cja=false&amp;chi=3&amp;ti=linux%E5%86%85%E6%A0%B8%E9%A9%B1%E5%8A%A8%20%E4%B9%8B%20module_init%E8%A7%A3%E6%9E%90%20%EF%BC%88%E4%B8%8A%EF%BC%89%20-%20CSDN%E5%8D%9A%E5%AE%A2&amp;cdo=-1&amp;dri=2&amp;dtm=HTML_POST"></iframe>

热门文章

  • linux内核驱动 之 module_init解析 (上)
    425
  • Linux内核很吊之 module_init解析 (下)
    95
  • 2017年提升之年
    59
  • 数组指针和指针数组的区别
    58
原创粉丝点击