由Kconfig这张地图的分布来看,PCI这块儿的代码应该分布在两个地方,drivers/pci和arch/i386/pci,两岸三地都属于一个中国,不管是drivers/pci那儿的,还是arch/i386/pci那儿的,也都只属于一个PCI子系统,本着一个中国的原则,咱们要统筹的全面的考察分析位于两个地方的代码,于是,这些远远突破了五位数的代码左看右看横看竖看都显得那么的阴森恐怖,不过人家咋说也是整个一PCI子系统,就像走在T台上的芙蓉姐姐和杨二车那姆一样,看起来恐怖但也是很有内涵的,岂能够让人三眼两眼三言两语就给看透了说透了?

那现在咱们就高瞻远瞩统筹全面的扫视一下这两个地方的代码,根据前面的内容可以推测对于USB、PCI这样的子系统都应该有一个subsys_initcall这样的入口,咱们得先找到它。朱德庸在《关于上班这件事》里说了,要花前半生找入口,花后半生找出口。可见寻找入口对于咱们这一生,对于看内核代码这件事儿都是无比重要的,当然寻找subsys_initcall这个入口是不用花前半生那么久的。下边儿俺就把找到的给列出来,为什么说“列”出来?难道还会有很多么?你猜对了,PCI这边儿入口格外多,而且是有预谋有组织成系列的,不单单有subsys_initcall,还有arch_initcall、postcore_initcall等等等等。

文件                                     函数                                         入口                        内存位置
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/probe.c             pcibus_class_init                     postcore_initcall       .initcall2.init
drivers/pci/proc.c               pci_proc_init                            __initcall                   .initcall6.init
arch/i386/pci/init.c             pci_access_init                        arch_initcall              .initcall3.init

看看那一列入口,形尽而意不同的种种xxx_initcall让人眼花缭乱的,真不知道该从哪儿下手,应了keso那句话:所有的痛苦都来自选择,所谓幸福,就是没有选择。像USB子系统那样子简简单单一个subsys_initcall,没得选择,傻强都知道怎么走。不过你迷惘一阵儿就可以了,可别真的被绕进去了。要知道“多少事,从来急;天地转,光阴迫。一万年太久,只争朝夕。四海翻腾云水怒,五洲震荡风雷激。要看清一切入口,全无敌。”咱们要只争朝夕看清一切入口的。

咱们已经知道对这些xxx_initcall函数的调用是必须按照一定顺序的,先调用.initcall1.init中的再调用.initcall2.init中的,很明显,表里列出来的应该最先被调用的是.initcall2.init子节中的两个函数pcibus_class_init和pci_driver_init。现在问题出现了,对于处于同一子节中的那些函数,比如pcibus_class_init和pci_driver_init这两个函数来说又是哪个会最先被调用?当然,你可以说处在前边儿地址的会最先被调用,这是大实话,因为do_initcalls函数的实现就是在.initcall.init所处的地址上来回的for循环。可你怎么知道同一子节的函数哪个在前边儿哪个在后边儿?

别的不多说,先看看gcc的Using the GNU Compiler Collection中的一段话:

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’.

看完这段话,希望会听到你说:我悟道了!更希望会看到你翻出来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函数之前被调用。再

给你看一句话,Documents/kbuild/makefile.txt的3.2中的:

The order of files in $(obj-y) is significant.

对于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文件里,它俩根本就不在同一个目录下面,所以前边儿判断pcibus_class_init和pci_driver_init的顺序的技巧并不适用,那有什么方法可以让咱们找出它们的顺序?看看王冉怎么说:“昨天是五一劳动节,可是全国都在放大假绝大多数人不劳动。可见,庆祝一件事的最好的方法就是不去做这件事。譬如,庆祝世界杯的最好的方式就是不去参加世界杯——中国队几乎一直都是这么做的。再譬如,庆祝情人节的最好的方式就是不去找情人——于是,很多中国的男人把情人节的前一天(2月13日)过成了情人节。”按他这说法,认清这俩函数之间顺序的最好方法就是不去管它们的顺序,俺可以点兵点将的随便点一个出来先说,不过作为一个很清楚自己责任和使命的80后,俺还是决定去发掘一下它们的顺序。

其实这个问题可以转化为arch/i386/pci下面的Makefile和drivers/pci下面的Makefile谁先谁后的问题,往大的方面说,就是内核是怎么构建的,也就是kbuild的问题。

内核里的Makefile主要有三种:第一种是根目录里的Makefile,它虽然只有一个,但地位远远凌驾于其它Makefile之上,里面定义了所有与体系结构无关的变量和目标;第二种是arch/*/Makefile,看到arch就知道它是与特定体系结构相关的,它包含在根目录下的Makefile中,为kbuild提供体系结构的特定信息,而它里面又包含了arch/*/下面各级子目录的那些Makefile;第三种就是密密麻麻躲在drivers/等各个子目录下边儿的那些Makefile了。

而kbuild构建内核的过程中,是首先从根目录Makefile开始执行,从中获得与体系结构无关的变量和依赖关系,并同时从arch/*/Makefile中获得体系结构特定的变量等信息,用来扩展根目录Makefile所提供的变量。此时kbuild已经拥有了构建内核需要的所有变量和目标,然后,Make进入各个子目录,把部分变量传递给子目录里的Makefile,子目录Makefile根据配置信息决定编译哪些源文件,从而构建出一个需要编译的文件列表。

然后,然后还有很漫长的路,你编译内核要耗多久,它就有多漫长,不过说到这儿前面问题的答案就已经浮出水面了,很明显,arch/i386/pci下面的Makefile是处在drivers/pci下面的Makefile前面的,也就是说,pci_access_init处在acpi_pci_init前面。

掌握了这些潜规则,我们在研究某个子系统时,就可以获得初始化函数的执行顺序,并按照该顺序使用韩峰同志对待日记的态度进行深入的分析。

Linux内核(13) - 子系统的初始化之以PCI子系统为例的更多相关文章

  1. Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

    原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理 ...

  2. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

  3. 如何查看linux内核中驱动的初始化顺序?

    答:通过生成的System.map可以查看到,主要关注__initcall_<module_entry_function>_init<level>,如: __initcall_ ...

  4. linux内核中有哪些子系统(框架)呢?

    注意: 分析用的linux内核版本为5.1.3 1. RTC子系统 2. Remote Processor子系统 3. Remote Processor Message子系统 4. SCSI子系统 5 ...

  5. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  6. Linux下C结构体初始化

    1.前言 今天在公司看一同事写的代码,代码中用到了struct,初始化一个struct用的是乱序格式,如下代码所示: typedef struct _data_t { int a; int b; }d ...

  7. linux 内核移植和根文件系统的制作【转载】

    原文地址:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 1.1 Linux内核基础知识 在动手进行Linux内核移植之 ...

  8. linux 内核移植和根文件系统的制作

    1.1 Linux内核基础知识 在动手进行Linux内核移植之前,非常有必要对Linux内核进行一定的了解,下面从Linux内核的版本和分类说起. 1.1.1  Linux版本 Linux内核的版本号 ...

  9. Linux 内核 链表 的简单模拟(1)

    第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...

随机推荐

  1. Java基础(十四):泛型

    一.Java 泛型: Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就 ...

  2. POJ 2763 Housewife Wind LCA转RMQ+时间戳+线段树成段更新

    题目来源:POJ 2763 Housewife Wind 题意:给你一棵树 2种操作0 x 求当前点到x的最短路 然后当前的位置为x; 1 i x 将第i条边的权值置为x 思路:树上两点u, v距离为 ...

  3. 【MySQL】MySQL-主从复制-集群方案-数据一致性问题解决方案 && MySQL备份的各种姿势

    1.写性能如何保证:分库分表 2.读性能如何保证:主从结构,实时备份 3.一致性问题怎么解决: 3.1.微博案例:Redis缓存,热数据查询走Redis,主从的延迟通过Redis消除 3.2.支付宝的 ...

  4. GBDT(Gradient Boost Decision Tree)

    原文:http://blog.csdn.net/aspirinvagrant/article/details/48415435 GBDT,全称Gradient Boosting Decision Tr ...

  5. jquery ui autocomplete 模拟百度搜索自动提示

    直接上代码 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset=" ...

  6. 空格在Html中的表示方法(&nbsp含义)

    转自:http://www.cnblogs.com/hailexuexi/archive/2010/07/25/1784611.html 在web开发经常会遇到如:   这样的字符.它其实是Html将 ...

  7. shell命令xargs解析

    1.多行变成单行 -bash-3.2# cat test.txt a b c d e f g o p q -bash-3.2# cat test.txt |xargs a b c d e f g o  ...

  8. ArcEngine中的缩放地图

    在ArcEngine地图操作中,缩放地图的功能经常用到,这里做一个小结. 缩放地图一般可分为以下几种情况: 1.缩放地图:与放大地图相对,一般是手动绘制区域或固定比例缩放,可调用命令或Expand函数 ...

  9. WebService 之 实例学习一

    新建一个空网站项目,添加新建项 “ Web 服务 ”. 一.WebServiceDemo.asmx 文件,默认内容如下: <%@ WebService Language="C#&quo ...

  10. 建maven私服nexus

    1.下载nexus 2.配置java环境 3.安装 C:\Program Files\nexus\nexus-2.11.4-01\bin\jsw\windows-x86-64 4.配置 http:// ...