由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. 巧妙使用div+css模拟表格对角线

    首先声明: 这只是探讨一种CSS模拟表格对角线的用法,实际在工作中可能觉得这样做有点小题大作,这不是本主题讨论的重点.如果对此深以为然的朋友,请一笑过之... 有时在插入文档时,要用到表格对角线,常见 ...

  2. Git Submodule使用完整教程

    Git Submodule功能刚刚开始学习可能觉得有点怪异,所以本教程把每一步的操作的命令和结果都用代码的形式展现给大家,以便更好的理解. 1.对于公共资源各种程序员的处理方式 每个公司的系统都会有一 ...

  3. Android实现图片轮显效果——自定义ViewPager控件

    一.问题概述 使用ViewPager控件实现可横向翻页.水平切换图片等效果,但ViewPager需要手动滑动才能切换页面,图片轮显效果的效果本质上就是在ViewPager控件的基础上让它能自动的进行切 ...

  4. Linux上如何查看Deb和RPM软件包的更新日志

    导读 当一个程序或库打包成Deb或RPM软件包后会有一些元数据文件包含在其中,其中之一就是 changelog文件,它记录了软件包每次更新后发生了什么变化.因此,如果你想找出你安装或更新的软件包发生了 ...

  5. php中120个内置函数

    php中实现事件模式 https://yq.aliyun.com/ziliao/162660 <?php class Event{ private $events = []; public fu ...

  6. POJ2762 Going from u to v or from v to u? 强连通+缩点

    题目链接: poj2762 题意: 给出一幅单向图.问这张图是否满足   随意两点ab 都能 从a到达b 或  从b到达a 题解思路: 推断一幅图是否满足弱连通 首先想到的是将图中的 强连通分量(能互 ...

  7. Android直播实现 Android端推流、播放

    最近想实现一个Android直播,但是对于这方面的资料都比较零碎,一开始是打算用ffmpeg来实现编码推流,在搜集资料期间,找到了几个强大的开源库,直接避免了jni的代码,集成后只用少量的java代码 ...

  8. Jacoco覆盖率工具使用调研

    JaCoCo Java Code Coverage Library Jacoco是一个开源的覆盖率工具.Jacoco可以嵌入到Ant .Maven中,并提供了EclEmma Eclipse插件,也可以 ...

  9. Missing styles. Is the correct theme chosen for this layout? Use the Theme combo box above the layou

    android无法静态显示ui效果. Missing styles. Is the correct theme chosen for this layout? Use the Theme combo ...

  10. JavsScript 之 求时间差

    var dateStart = new Date(); //开始时间var dateEnd = new Date(); //结束时间 var timePeriod = dateEnd.getTime( ...