码迷,mamicode.com
首页 > 其他好文 > 详细

GCC特性之__init修饰解析

时间:2015-03-13 19:05:14      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:linux

         做底层驱动的人都知道,在driver文件中会经常看见“__init“修饰的代码,那么__init标记有什么意义?
        下面我们就来看看。

        在GCC拓展的特殊属性中section时会提及这个__init,他所修饰的所有代码都会放到.init.text节中,当初始化结束后就可以释放这部分内存,这样就减少了内存的占用空间。
        那么另外一个问题就来了,什么时候调用到该函数?
        要回答这个问题首先要先讲解这个宏:subsys_initcall,在文件kernel/include/linux/init.h中。定义方式如下:
        #define subsys_initcall(fn)             __define_initcall("4",fn,4)

        又带来了一个新的宏__define_initcall,定义方式如下:
        #define __define_initcall(level,fn,id) \
                 static initcall_t __initcall_##fn##id __used \
                 __attribute__((__section__(".initcall" level ".init"))) = fn


       那么 __define_initcall是用来修饰将指定的函数指针fn存放到".initcall.init"节中。因此可以断定subsys_initcall宏将fn存放到“.initcall.init”节中的“..initcall4.init”里。

        我们需要理解一下.initcall.init和.init.text和".initcall4.init"之类的符号的含义,这就需要我们了解和内核可执行文件的相关概念。
什么是内核可执行文件?
        可执行文件映像中包含了进程执行的代码和数据,同时也包含了操作系统用来将映像正确装入内存并执行的信息。具体信息大家可以上网搜索,这里就一笔带过。
        这些信息包含了如下文本段、数据段、init数据段、bass段等。这些数据都是由一个称为“链接器脚本”的文件链接并装入的,这个文件的功能时间这些输 入信息的各段装入到指定的地址处。vmlinux.lds就是存在"arch/xxx/"目录中哦你的内核连接其脚本,他负责链接内核的各个段并将他们装 入到内存中特定偏移量处。
         下面让我们看一下这个链接器脚本文件,路径是"arch/arm/kernel",名称为vmlinux.lds.
          找到关键程序:INIT_SETUP(16)--->init_main.c的__setup_start指向的.init.setup节的开始
                                       INIT_CALLS---->init_main.c的__initcall_start指向的.initcallearly.init节的开始
                                      CON_INITCALL---->./drivers/char/tty_io.c的__con_initcall_start指向的.con_initcall.init节的开始。
                                      其他的宏就不讲解了。
其中INIT_CALL是主要的,分为了九段:
                 INIT_CALLS_LEVEL(0)                                     \
                 INIT_CALLS_LEVEL(1)                                     \
                 INIT_CALLS_LEVEL(2)                                     \
                 INIT_CALLS_LEVEL(3)                                     \
                 INIT_CALLS_LEVEL(4)                                     \
                 INIT_CALLS_LEVEL(5)                                     \
                 INIT_CALLS_LEVEL(rootfs)                                \
                 INIT_CALLS_LEVEL(6)                                     \
                 INIT_CALLS_LEVEL(7)
而INIT_CALL_LEVEL宏定义如下:
#define INIT_CALLS_LEVEL(level)                                         \
                 VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
                 *(.initcall##level##.init)                              \
                 *(.initcall##level##s.init)

实 际就是.initcall0.init~.initcall7.init。那么subsys_initcall将值定的话函数指针放在 了".initcall4.init"子段中。例如:device_initcall将函数指针存放到了".initcall6.init" 中;core_initcall将函数指针存放到了".initcall1.init"子段中。
        各个子段的顺序是确定的,是先调用".initcall4.init"中的函数指针再去执行“.initcall5.init”中的函数指针。

        那么__init修饰的初始化函数在内核初始化过程中调用的顺序和.initcall.init里面的函数指针的顺序有关,因此不同的初始化函数是被放在不同的子段中,因此就决定了他们的调用顺序。

本文出自 “Springtimes_hy” 博客,请务必保留此出处http://springtimes.blog.51cto.com/2681367/1620123

GCC特性之__init修饰解析

标签:linux

原文地址:http://springtimes.blog.51cto.com/2681367/1620123

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!