Linux那些事儿 之 我是PCI(4)初始化(一)

解析完了PCI的那些内核参数,再翻过多少座山跨过多少条河,内核就会遇到init/main.c里一个名叫do_initcalls的函数。do_initcalls对内核来说只不过是漫长冒险旅程中的一个驿站,对PCI这个故事来说却是命运转轮的开始,内核在它里边完成了对.initcall.init节里各种xxx_initcall函数的执行,PCI的那些自然也包括在内。你不用像新东方老罗“我走来走去,为中国的命运苦苦思索。”那样走来走去为PCI的命运思索,因为决定PCI命运的那些xxx_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

咱们从讲USB Core时就已经知道对这些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会首先被调用,那咱们就先窥视一下它的庐山真面目
100 static struct class pcibus_class = {
101          .name           = "pci_bus",
102          .release        = &release_pcibus_dev,
103 };
104
105 static int __init pcibus_class_init(void)
106 {
107          return class_register(&pcibus_class);
108 }
class_register是设备模型中一个很基础的函数,在这里它的目的就是注册一个名叫“pci_bus”的class,关于class,你应该不会感到陌生了,usb那里就已经注册过一个usb_host,不过不同的是那时使用的是class_create,而现在使用的是class_register,咱不陪写代码的哥们儿玩这些文字游戏了,不管它是create还是register,咱只看它们能够带来啥后果,当然所谓的后果要体现在sysfs上,所以去look一下/sys/class目录

atm             dma             graphics   hwmon            i 2c -adapter       input   

mem            misc             net         pci_bus           scsi_device       scsi_disk

scsi_host     sound           spi_host   spi_master       spi_transport     tty     

usb_device   usb_endpoint usb_host  vc                  vtconsole

想当初,usb子系统初始化的时候,调用了一次class_create(THIS_MODULE, "usb_host"),然后上边儿就多了一个usb_host目录,那么现在调用这个class_register,上边儿又会多出什么?这个大家一眼就能看出来,即使一眼看不出来两眼也能看出来了,赚钱买猪肉的本事没有,寻找这种敏感地带的本事还都是有的,凭空多出来的就是那个pci_bus。从这点儿看,create还是register对咱们来说都差不多,都是在/sys/class下边儿创建了一个类,usb_host类的目录里是各个具体的主机控制器,pci_bus类的目录里对应的就是各个pci总线了。本来难得糊涂一下明白这些就成了,不过如果真想稍微不那么糊涂一点儿,可以去扫两眼class_create的定义,你就会发现它里面最终也会调用一个class_register,这两个的差别就是class_create要更傻瓜一些,你指定个类的名称就可以调用它了,它里面会帮你创建一个struct class结构体,而class_register则更费事一些,你需要自己亲自动手创建一个struct class结构体。如果你觉得自己挺特殊,需要指定自己的release函数等,那就必须得使用class_register了,PCI就属于这种情况,至于它怎么个特殊,就是后话了。

pcibus_class_init之后,接着就应该是pci_driver_init

542 struct bus_type pci_bus_type = {

543         .name           = "pci",

544         .match          = pci_bus_match,

545         .uevent         = pci_uevent,

546         .probe          = pci_device_probe,

547         .remove         = pci_device_remove,

548         .suspend        = pci_device_suspend,

549         .suspend_late   = pci_device_suspend_late,

550         .resume_early   = pci_device_resume_early,

551         .resume         = pci_device_resume,

552         .shutdown       = pci_device_shutdown,

553         .dev_attrs      = pci_dev_attrs,

554 };

555

556 static int __init pci_driver_init(void)

557 {

558         return bus_register(&pci_bus_type);

559 }

560

561 postcore_initcall(pci_driver_init);

还记得linux设备模型里存在于总线、设备、驱动之间的那个著名的三角关系么?如果不记得,那就先听俺讲个小故事:

     话说多少年以前有个人非常的健忘,他老婆很无奈,就对他说:“听说南村的谁谁谁专治女性不孕男性健忘,你还是去找他医一下吧!” 好男人准则第一条就是要听老婆的话,于是这个人就背上弓箭,骑上马出发了。人不是都有三急么,半道儿上他想大便,就把马拴在一棵大树上,躲在树后,顺手把箭插在地上。方便过后,正在顺爽,顺爽,一顺再顺,顺出新自我,忽然看见了地上的箭,惊出了一身冷汗,“好险,不知谁射来的箭,差点要了我的小命!”紧赶的往外跑,一脚踩在大便上,不禁连皱眉头,大骂不已:“谁这么缺德,不讲公共卫生,在这里随地大小便……”。等到看到拴在树上的马,又高兴起来,心想:虽然吃了点儿苦头,捡到一匹马着实得美,就像金帝美滋滋巧克力,全新的色彩,全新的味道。于是他骑上马晕晕乎乎不知所之,沿着原路折了回去。一边想着我这是在哪儿呢,一边瞧见了一座房子。“咦,这房子好生面熟?……”这个时候,他老婆正从屋里看见他糊里糊涂的样子,气不打一处来,出门儿来责备他。只见他不卑不亢的作了一个揖,说:“这位大嫂,你我素昧平生,何苦出言不逊?”

这个故事的教育意义就在于告诉我们健忘是一种病态,善忘是一种境界,做人不能健忘到如此地步。那个三角关系中的总线落实在USB就是usb_bus_type,落实在PCI就是上面的pci_bus_type,pci_driver_init函数的目的就是注册PCI总线,只有总线存在了,才会有设备的那条链表和驱动的那条链表,才会有设备和驱动之间的match。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值