__initcall_start 调用的方式 加载模块
2013-08-18 15:35
309 查看
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
__initcall_end = .;
http://book.51cto.com/art/201007/213623.htm
4.5.3 分析示例
这里以PCI子系统为例,分析一下它的初始化都使用了上述的哪些宏标记。
与很多子系统不同,PCI子系统的实现代码分布在内核代码树的两个地方,除去drivers/pci存放了体系结构无关部分的代码之外,还有arch/i386/pci存放了体系结构相关部分的代码,因此我们需要在这两个地方分别查找它的初始化代码。
如表4.1所示为PCI子系统的初始化代码分布情况。
PCI子系统的初始化几乎使用了本节所描述的所有xxx_initcall宏,它的初始化也严格按照表4.1所描述的内存存放顺序执行。
表4.1 PCI子系统初始化代码分布情况
但是,我们可以从表4.1中发现,PCI子系统的一些初始化函数位于同一子节,前面只是讲述了不同子节之间的函数按照子节的优先级顺序执行,并没有讲述同一子节函数之间的调用顺序。
当然,我们可以指出一个事实,同一子节之间,地址位于最前面的函数会首先被调用。但是我们并不知道哪个函数位于前边、哪个函数位于后边,比如同样位于.initcall2.init子节的pcibus_class_init函数和pci_driver_init函数,有没有一个简单的方法来进行判断?
下面是GCC手册中的一段话。
即是说,连接器按照库文件和目标文件被指定的顺序进行处理,打开pcibus_class_init函数和pci_driver_init函数所在目录drivers/pci/下的Makefile文件,可以看到:
probe.o在pci- driver.o之前被指定,因此probe.c文件中的pcibus_class_init函数将在pci- driver.c文件中的pci_driver_init函数之前被调用。
对于pcibus_class_init函数和pci_driver_init函数这样位于同一目录位置的可以通过该目录Makefile文件指定的链接顺序来判断,而对于.initcall3.init子节中的acpi_pci_init函数和pci_access_init函数则不能使用这个方法。
acpi_pci_init函数位于drivers/pci/pci-acpi.c文件,而pci_access_init 函数位于arch/i386/pci/init.c文件,它们位于不同的目录,此时问题即转化为arch/i386/pci下的Makefile和drivers/pci下的Makefile谁先谁后的问题,这就涉及kbuild构建内核的运行机制。
内核中的Makefile主要有如下3种。
内核源码树根目录里的Makefile。虽说只有一个,但地位远远高于其他Makefile,其中定义了所有与体系结构无关的变量和目标。
arch/*/Makefile。与特定体系结构相关,它会被根目录下的Makefile包含,为kbuild提供体系结构的特定信息。而它又包含了arch/*/目录下面各级子目录下的那些Makefile。
drivers/等各个子目录下的那些Makefile。
kbuild构建内核时,首先从根目录Makefile开始执行,从中获得与体系结构无关的变量和依赖关系,并同时从arch/*/Makefile中获得体系结构特定的变量等信息,用来扩展根目录Makefile所提供的变量。
此时,kbuild已经拥有了构建内核需要的所有变量和目标。然后,kbuild进入各个子目录,把部分变量传递给子目录里的Makefile,子目录Makefile根据配置信息决定编译哪些源文件,从而构建出一个需要编译的文件列表。
之后,即是内核编译的漫长过程。现在,很明显,arch/i386/pci下的Makefile是在drivers/pci下的Makefile之前被执行,即是说,pci_access_init函数在acpi_pci_init函数之前被执行。
掌握这些规则,我们在研究某个子系统时,即可获得初始化函数的执行顺序,并按照该顺序进行深入的分析。
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
__initcall_end = .;
http://book.51cto.com/art/201007/213623.htm
4.5.3 分析示例
这里以PCI子系统为例,分析一下它的初始化都使用了上述的哪些宏标记。
与很多子系统不同,PCI子系统的实现代码分布在内核代码树的两个地方,除去drivers/pci存放了体系结构无关部分的代码之外,还有arch/i386/pci存放了体系结构相关部分的代码,因此我们需要在这两个地方分别查找它的初始化代码。
如表4.1所示为PCI子系统的初始化代码分布情况。
PCI子系统的初始化几乎使用了本节所描述的所有xxx_initcall宏,它的初始化也严格按照表4.1所描述的内存存放顺序执行。
表4.1 PCI子系统初始化代码分布情况
文 件 | 初始化函数 | 宏 标 记 | 内 存 位 置 |
arch/i386/pci/acpi.c | pci_acpi_init | subsys_initcall | .initcall4.init |
arch/i386/pci/common.c | pcibios_init | subsys_initcall | .initcall4.init |
arch/i386/pci/i386.c | pcibios_assign_resources | fs_initcall | .initcall5.init |
arch/i386/pci/legacy.c | pci_legacy_init | subsys_initcall | .initcall4.init |
drivers/pci/pci-acpi.c | acpi_pci_init | arch_initcall | .initcall3.init |
drivers/pci/pci- driver.c | pci_driver_init | postcore_initcall | .initcall2.init |
drivers/pci/pci- sysfs.c | pci_sysfs_init | late_initcall | .initcall7.init |
drivers/pci/pci.c | pci_init | device_initcall | .initcall6.init |
drivers/pci/proc.c | pci_proc_init | __initcall | .initcall6.init |
arch/i386/pci/init.c | pci_access_init | arch_initcall | .initcall3.init |
当然,我们可以指出一个事实,同一子节之间,地址位于最前面的函数会首先被调用。但是我们并不知道哪个函数位于前边、哪个函数位于后边,比如同样位于.initcall2.init子节的pcibus_class_init函数和pci_driver_init函数,有没有一个简单的方法来进行判断?
下面是GCC手册中的一段话。
the linker searches and processes libraries and object files in the order they are specified. Thus, 'foo.o -lz bar.o' searches library 'z' after file 'foo.o' but before 'bar.o'.
即是说,连接器按照库文件和目标文件被指定的顺序进行处理,打开pcibus_class_init函数和pci_driver_init函数所在目录drivers/pci/下的Makefile文件,可以看到:
5 obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ 6 pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
probe.o在pci- driver.o之前被指定,因此probe.c文件中的pcibus_class_init函数将在pci- driver.c文件中的pci_driver_init函数之前被调用。
对于pcibus_class_init函数和pci_driver_init函数这样位于同一目录位置的可以通过该目录Makefile文件指定的链接顺序来判断,而对于.initcall3.init子节中的acpi_pci_init函数和pci_access_init函数则不能使用这个方法。
acpi_pci_init函数位于drivers/pci/pci-acpi.c文件,而pci_access_init 函数位于arch/i386/pci/init.c文件,它们位于不同的目录,此时问题即转化为arch/i386/pci下的Makefile和drivers/pci下的Makefile谁先谁后的问题,这就涉及kbuild构建内核的运行机制。
内核中的Makefile主要有如下3种。
内核源码树根目录里的Makefile。虽说只有一个,但地位远远高于其他Makefile,其中定义了所有与体系结构无关的变量和目标。
arch/*/Makefile。与特定体系结构相关,它会被根目录下的Makefile包含,为kbuild提供体系结构的特定信息。而它又包含了arch/*/目录下面各级子目录下的那些Makefile。
drivers/等各个子目录下的那些Makefile。
kbuild构建内核时,首先从根目录Makefile开始执行,从中获得与体系结构无关的变量和依赖关系,并同时从arch/*/Makefile中获得体系结构特定的变量等信息,用来扩展根目录Makefile所提供的变量。
此时,kbuild已经拥有了构建内核需要的所有变量和目标。然后,kbuild进入各个子目录,把部分变量传递给子目录里的Makefile,子目录Makefile根据配置信息决定编译哪些源文件,从而构建出一个需要编译的文件列表。
之后,即是内核编译的漫长过程。现在,很明显,arch/i386/pci下的Makefile是在drivers/pci下的Makefile之前被执行,即是说,pci_access_init函数在acpi_pci_init函数之前被执行。
掌握这些规则,我们在研究某个子系统时,即可获得初始化函数的执行顺序,并按照该顺序进行深入的分析。
相关文章推荐
- 模块已加载,但对dllregisterServer的调用失败
- core_initcall如何调用
- 函数调用方式--__thiscall调用方式和__cdecl,__stdcall有什么区别
- win7下修改注册表问题 模块"jscript.dll"已加载,但对DllRegisterServer的调用失败,错误代码为0x80004005 我是WIN7
- Android -- service的开启方式, start开启和绑定开启服务,调用服务的的方法, aidl调用远程服务
- 模块接口间三中调用方式
- 模块"XX.dll"已加载,但对DllRegisterServer的调用失败
- C#换机器后调用dll失败提示无法加载DLL找不到指定的模块
- wiredep----另一种思路的前端模块加载方式
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- Android -- service的开启方式, start开启和绑定开启服务,调用服务的的方法, aidl调用远程服务
- 转:PHP教程之PHP调用session_start后页面始终加载的问题研究
- 函数调用方式--__thiscall调用方式和__cdecl,__stdcall有什么区别
- 模块替换方式实现添加系统调用
- 关于__initcall_start的研究(转贴!)
- linux模块(module_init)、子系统(subsys_initcall)入口函数详解
- 函数调用方式--__thiscall调用方式和__cdecl,__stdcall有什么区别
- 模块已加载,但对dllregisterServer的调用失败
- 以自定义方式加载 lua 模块
- 【原创】Linux驱动入门之以模块方式加载驱动