__initcall_start 调用的方式 加载模块

__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子系统初始化代码分布情况

文    件

初始化函数

宏  标  记

内 存 位 置

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


但是,我们可以从表4.1中发现,PCI子系统的一些初始化函数位于同一子节,前面只是讲述了不同子节之间的函数按照子节的优先级顺序执行,并没有讲述同一子节函数之间的调用顺序。

当然,我们可以指出一个事实,同一子节之间,地址位于最前面的函数会首先被调用。但是我们并不知道哪个函数位于前边、哪个函数位于后边,比如同样位于.initcall2.init子节的pcibus_class_init函数和pci_driver_init函数,有没有一个简单的方法来进行判断?

下面是GCC手册中的一段话。

 
  
  1. 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文件,可以看到:

 
  
  1. 5 obj-y           += access.o bus.o probe.o  remove.o pci.o quirks.o \  
  2. 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函数之前被执行。

掌握这些规则,我们在研究某个子系统时,即可获得初始化函数的执行顺序,并按照该顺序进行深入的分析。



你可能感兴趣的:(Linux)