Linux那些事儿 之 戏说USB(12)从这里开始

原创 2007年09月21日 00:48:00
任小强们说房价高涨从现在开始,股评家们说牛市从5000点开始。他们的开始需要我们的钱袋,我的开始只需要一台电脑,最好再有一杯茶,伴着几支小曲儿,不盯着钱总是会比较惬意的。生容易,活容易,生活不容易,因为要盯着钱。
USB core从USB子系统的初始化开始,我们也需要从那里开始,它们在文件drivers/usb/core/usb.c
938 subsys_initcall(usb_init);
939 module_exit(usb_exit);
我们看到一个subsys_initcall,复旦人甲说它也是一个宏,我们可以把它理解为module_init,只不过因为这部分代码比较核心,开发者们把它看作一个子系统,而不仅仅是一个模块,这也很好理解,usbcore这个模块它代表的不是某一个设备,而是所有usb设备赖以生存的模块,Linux中,像这样一个类别的设备驱动被归结为一个子系统。比如pci子系统,比如scsi子系统,基本上,drivers/目录下面第一层的每个目录都算一个子系统,因为它们代表了一类设备。subsys_initcall(usb_init)的意思就是告诉我们usb_init是我们真正的初始化函数,而usb_exit()将是整个usb子系统的结束时的清理函数,于是我们就从usb_init开始看起。
既然复旦人甲都这么说了,那咱们就从usb_init开始看起吧。至于子系统在内核里具体的描述,牵涉到linux设备模型了,可以去看ldd3,或者更详细的。目前来说,我们只需要知道子系统通常显示在sysfs分层结构中的顶层,比如块设备子系统对应/sys/block,当然也不一定,usb子系统对应的就是/sys/bus/usb。
860 /*
861  * Init
862  */
863 static int __init usb_init(void)
864 {
865         int retval;
866         if (nousb) {
867                 pr_info("%s: USB support disabled/n", usbcore_name);
868                 return 0;
869         }
870
871         retval = ksuspend_usb_init();
872         if (retval)
873                 goto out;
874         retval = bus_register(&usb_bus_type);
875         if (retval)
876                 goto bus_register_failed;
877         retval = usb_host_init();
878         if (retval)
879                 goto host_init_failed;
880         retval = usb_major_init();
881         if (retval)
882                 goto major_init_failed;
883         retval = usb_register(&usbfs_driver);
884         if (retval)
885                 goto driver_register_failed;
886         retval = usb_devio_init();
887         if (retval)
888                 goto usb_devio_init_failed;
889         retval = usbfs_init();
890         if (retval)
891                 goto fs_init_failed;
892         retval = usb_hub_init();
893         if (retval)
894                 goto hub_init_failed;
895         retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
896         if (!retval)
897                 goto out;
898
899         usb_hub_cleanup();
900 hub_init_failed:
901         usbfs_cleanup();
902 fs_init_failed:
903         usb_devio_cleanup();
904 usb_devio_init_failed:
905         usb_deregister(&usbfs_driver);
906 driver_register_failed:
907         usb_major_cleanup();
908 major_init_failed:
909         usb_host_cleanup();
910 host_init_failed:
911         bus_unregister(&usb_bus_type);
912 bus_register_failed:
913         ksuspend_usb_cleanup();
914 out:
915         return retval;
916 }
看到上面定义里的__init标记没,写过驱动的应该不会陌生,它对内核来说就是一种暗示,表明这个函数仅在初始化期间使用,在模块被装载之后,它占用的资源就会释放掉用作它处。它的暗示你懂,可你的暗示,她却不懂或者懂装不懂,多么让人感伤。它在自己短暂的一生中一直从事繁重的工作,吃的是草吐出的是牛奶,留下的是整个USB子系统的繁荣。
受这种精神所感染,我觉得还是有必要为它说的更多些,21世纪多的是任小强,缺的是知恩图报的人。对__init的定义在include/linux/init.h里
43 #define __init          __attribute__ ((__section__ (".init.text")))
好像这里的疑问要更多,不过与__init相比,这点辛苦算什么,我会在它强大的精神支持下尽量说清楚的。那么__attribute__是什么?Linux内核代码使用了大量的GNU C扩展,以至于GNU C成为能够编译内核的唯一编译器,GNU C的这些扩展对代码优化、目标代码布局、安全检查等方面也提供了很强的支持。而__attribute__就是这些扩展中的一个,它主要被用来声明一些特殊的属性,这些属性主要被用来指示编译器进行特定方面的优化和更仔细的代码检查。GNU C支持十几个属性,section是其中的一个,我们查看gcc的手册可以看到下面的描述
‘section ("section-name")'   Normally, the compiler places the code it generates in the `text'
     section. Sometimes, however, you need additional sections, or you
need certain particular functions to appear in special sections.
   The `section' attribute specifies that a function lives in a
   particular section. For example, the declaration:
 
      extern void foobar (void) __attribute__ ((section ("bar")));

   puts the function ‘foobar' in the ‘bar' section.
 
 
       Some file formats do not support arbitrary sections so the
   ‘section' attribute is not available on all platforms. 
If you
  
need to map the entire contents of a module to a particular
   section, consider using the facilities of the linker instead.

通常编译器将函数放在.text节,变量放在.data 或 .bss 节,使用section属性,可以让编译器将函数或变量放在指定的节中。那么前面对__init的定义便表示将它修饰的代码放在.init.text节。连接器可以把相同节的代码或数据安排在一起,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。
那内核又是如何调用到这些__init修饰的初始化函数那?好奇心是科学的原动力,茶叶蛋就是这么煮出来的,原子弹也是这么造出来的,__init背后的哲学总不会比它们还难,芙蓉姐姐说了,我挑战,我喜欢。好像越聊越远了,不过想想多少年前当自己还是青涩少年的时候就对它极度的好奇过,这里还是尽量将它说一下,也顺便积攒下rp,稍后的冠军杯里俺的米兰也好有个开门红。
要回答这个问题,还需要回顾一下上面938行的代码,那里已经提到subsys_initcall也是一个宏,它也在include/linux/init.h里定义
125 #define subsys_initcall(fn)             __define_initcall("4",fn,4)

这里又出现了一个宏__define_initcall,它是用来将指定的函数指针fn放到initcall.init节里,也在include/linux/init.h文件里定义,这里就不多说了,有那点意思就可以了。而对于具体的subsys_initcall宏,则是把fn放到.initcall.init的子节.initcall4.init里。要弄清楚.initcall.init、.init.text和.initcall4.init这样的东东,我们还需要了解一点内核可执行文件相关的概念。

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

涉及到的东西越来越多了是吧,先深呼吸,平静一下,坚定而又勇敢的打开arch/i386/kernel/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_start指向.initcall.init节的开始,__initcall_end指向它的结尾。而.initcall.init节又被分为了7个子节,分别是

.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init

我们的subsys_initcall宏便是将指定的函数指针放在了.initcall4.init子节。其它的比如core_initcall将函数指针放在.initcall1.init子节,device_initcall将函数指针放在了.initcall6.init子节等等,都可以从include/linux/init.h文件找到它们的定义。各个字节的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,等等。__init修饰的初始化函数在内核初始化过程中调用的顺序和.initcall.init节里函数指针的顺序有关,不同的初始化函数被放在不同的子节中,因此也就决定了它们的调用顺序。

至于实际执行函数调用的地方,就在/init/main.c文件里,内核的初始化么,不在那里还能在哪里,里面的do_initcalls函数会直接用到这里的__initcall_start、__initcall_end来进行判断,不多说了。我们的思念已经入滔滔江水泛滥成灾了,还是回到久违的usb_init函数吧。

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

解析完了PCI的那些内核参数,再翻过多少座山跨过多少条河,内核就会遇到init/main.c里一个名叫do_initcalls的函数。do_initcalls对内核来说只不过是漫长冒险旅程中的一个驿站...
  • mrwangwang
  • mrwangwang
  • 2014年06月12日 17:31
  • 1188

Linux那些事儿 之 我是PCI(5)初始化(二)

.initcall2.init子节中的两个函数已经见识过了,该轮到.initcall3.init子节里的了,就是上边儿表中的acpi_pci_init和pci_access_init,这两个又是谁先谁...
  • mrwangwang
  • mrwangwang
  • 2014年06月12日 18:51
  • 2343

细说网络那些事儿之网络基本功(一):细说网络传输

文章转自:https://community.emc.com/thread/197851
  • n289950578
  • n289950578
  • 2014年08月11日 14:25
  • 3450

《Linux那些事儿之我是USB》我是U盘(12)梦开始的地方

对于整个usb-storage模块,usb_stor_init()函数是它的开始,然而,对于U盘驱动程序来说,它真正驱使U盘工作却是始于storage_probe()函数。 两条平行线只要相交,就注...
  • fudan_abc
  • fudan_abc
  • 2011年09月18日 22:57
  • 7762

Linux那些事儿 之 戏说USB(6)好戏开始了

新气象要用新代码,就使用最新的3.18.2版本的内核了。首先要去drivers/usb目录下走一走看一看。 atm class core gadget host image misc mon seri...
  • zhqh100
  • zhqh100
  • 2015年03月24日 14:00
  • 914

Linux那些事儿 之 戏说USB(20)设备的生命线(三)

函数usb_control_msg调用了usb_internal_control_msg之后就去一边儿睡大觉了,脏活儿累活儿,全部留给usb_internal_control_msg去做了,这才叫骨干...
  • zhqh100
  • zhqh100
  • 2015年03月25日 15:37
  • 684

Linux那些事儿 之 戏说USB(30)驱动的生命线(二)

core配置设备使用的是message.c里的usb_set_configuration函数 int usb_set_configuration(struct usb_device *dev, int...
  • zhqh100
  • zhqh100
  • 2015年03月26日 20:17
  • 675

Linux那些事儿 之 戏说USB(13)接口是设备的接口(二)

前面struct usb_interface里表示接口设置的struct usb_host_interface就被有意无意的飘过了,咱们在这里看看它的真面目,同样在include/linux/usb....
  • zhqh100
  • zhqh100
  • 2015年03月24日 17:03
  • 707

Linux那些事儿 之 戏说USB(7)不一样的core

使用命令lsmod,看看它的输出,然后找这么个模块usbcore,不要说你找不到,我不会相信的。它是什么?它就是咱们这里要说的usb系统的核心,如果要在linux里使用usb,这个模块是必不可少的,另...
  • zhqh100
  • zhqh100
  • 2015年03月24日 14:12
  • 764

Linux那些事儿 之 戏说USB(4)最终奥义

一个完整的USB系统应该实现上面图里的各个部分,图里主要显示了四个层次,USB物理设备(USB Physical Device)、客户软件(Client SW)、USB系统软件(USB System ...
  • zhqh100
  • zhqh100
  • 2015年03月24日 10:40
  • 531
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux那些事儿 之 戏说USB(12)从这里开始
举报原因:
原因补充:

(最多只允许输入30个字)