Linux那些事儿 之 我是PCI(2)PCI全接触 – fudan

关于这张图,你只要注意到__setup_start、__setup_end、__start___param、__stop___param这几个东东就可以了,它们很明确的告诉我们特权参数和普通参数在内存里存放的位置是不一样的,特权参数放在__setup_start和__setup_end之间的节里,普通参数放在__start___param和__stop___param之间的节里。

PCI里边儿也出现了一个early_param,在drivers/pci/pci.c里面

1425early_param("pci", pci_setup);

early_param怎么定义的你可以去include/linux/init.h里面看,俺这里就不说了,反正这行的意思就是发现你grub文件的kernel行里有“pci=”这样的东东的时候,就会调用函数pci_setup进行处理,怎么处理?这都是以后的事儿了。

参数解析完之后,再经过一个漫长而曲折的过程,就会调用到各种各样的xxx_initcall入口函数,这些函数的调用不是随便的,而是按照一定顺序的,这个顺序就取决于__define_initcall宏。__define_initcall宏用来将指定的函数指针放到.initcall.init节里,这都是讲USB Core的时候说过的,再扼要重述一遍。

内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init 数据、bass 等等。这些对象文件都是由一个称为链接器脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于 arch/<target>/ 目录中的内核链接器脚本,它负责链接内核的各个节并将它们装入内存中特定偏移量处。在vmlinux.lds文件里查找initcall.init就可以看到下面的内容

__inicall_start = .;

.initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {

*(.initcall1.init)

*(.initcall2.init)

*(.initcall3.init)

*(.initcall4.init)

*(.initcall5.init)

*(.initcall6.init)

*(.initcall7.init)

}

__initcall_end = .;

这就告诉我们.initcall.init节又分成了7个字节,而xxx_initcall入口函数指针具体放在哪一个子节里边儿是由xxx_initcall的定义中,__define_initcall宏的参数决定的,比如core_initcall将函数指针放在.initcall1.init子节,device_initcall将函数指针放在了.initcall6.init子节等等。各个子节的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,等等。不同的入口函数被放在不同的子节中,因此也就决定了它们的调用顺序。

关于内核里初始化有关的一些内存节,可以再参考下《Understanding Linux Network Internals》的图7-3

现在你再回头去看看前面那张描述PCI里边儿各种xxx_initcall入口的表,应该不会再觉得迷惘了,对整个PCI子系统的运作流程也大致有个谱了。

看着你手中的戒指,你说,你可以把它取下来吗?

Linux那些事儿 之 我是PCI(2)PCI全接触 – fudan

相关文章:

你感兴趣的文章:

标签云: