`
qiaoweishu
  • 浏览: 66209 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

【zz】Linux启动过程中硬件模块的加载

阅读更多
文章来源不详。

    阅读Linux内核启动代码的直接动力是我想编写RTL8019AS的网卡驱动程序(2.4.18内核只支持了CS8900A)。既然要写驱动,我就想知道它是怎么样被加载的,好奇心驱使我先去搞定这个问题。

    拿到2.4.18的软件包,一万多个文件,我不知怎么下手。所幸手头有这么三件工具助我入门:

1,一块移植好linux的开发板,通过它可以看到linux启动过程打印的消息。

2, google,网上关于linux的资料真是太多了!!!

3, Windows文件搜索引擎,通过它可以知道在那些文件中打印出那些消息。

    很快,我就找到了linux启动的总的入口,/arch/arm/boot/compressed/head.s。

    head.s完成的工作主要是底层寄存器、MMU的一些设定以及kernel的解压缩。汇编文件中调用的C代码大多位于该目录下misc.c文件,比如decompress_kernel。

    当然,这部分不是重点,head执行完毕以后就跳到start_kernel(),这才是我们的重点所在,这个函数位于文件/init/main.c中。这个文件是启动的主线!!!

    在start_kernel中,依次执行各个初始话函数,这里具体我没有看,一直到最后rest_init(),在这个函数里启动了一个init线程,而主线程自己则进入了IDLE状态。所以我们关心一下init线程做了什么事情,看文件最后init函数。

    在这个函数里面,先lock_kernel,然后调用do_basic_setup,在这个函数里面又是一堆的初始化,有一个函数要引起我们的注意:do_initcalls。看看它干了什么:(这之后的东西在下文文件系统中讲解)
    static void __init do_initcalls(void)

    {

              initcall_t *call;

              call = &__initcall_start;

              do {

                     (*call)( );
                     call++;

              } while (call < &__initcall_end);

              /* Make sure there is no pending stuff from the initcall sequence */
              flush_scheduled_tasks();

    }


    很难相信,我们关心的外围模块的驱动就是被这一段程序加载的。怎么回事?我们慢慢来看:

    首先看__initcall_start和__initcall_end,找遍了所有C代码,没有它们的定义。后来在vmlinux-armv.lds.in文件中找到了它们:

__initcall_start = .;

*(.initcall.init)

__initcall_end = .;

    这个文件是和link相关的文件,它决定代码在load环境中的位置,就好比ADS中的scf文件。我们还是先看.initcall.init的含义吧,它在/include/linux/init.h中定义:

#define __init_call __attribute__ ((unused,__section__ (".initcall.init ")))

    参考GCC说明,这段话的意思就是说所有以 __init_call前缀定义的函数在链接过程中都放到名字为.initcall.init的段(section)里面。OK,有点味道了,也就是说,如果我们给一个函数冠以__init_call,那么它在编译链接的时候就会放到.initcall.init这个段里面。而上面这段循环所做的事情就很清楚了,它从段的首地址开始,依次执行每一个函数,直到段尾为止。

    这个时候,我们应该在想,那些要注册的外围模块的初始化程序是不是都是定义成__init_call类型的呢?正如我们所料,查看各个模块我们会发现其初始化函数x会被定义成为module_init(x),在/include/linux/init.h中它定义如下:

       #define module_init(x) __initcall(x);

#define __initcall(fn)    static initcall_t __initcall_##fn __init_call = fn

    这段代码说module_init(x)等价于__initcall(x),而__initcall(x)表示函数x是静态的具有__init_call 性质的函数(这里名字比较多,容易看乱),因此在链接时,它会被放在.initcall.init段中。只要x函数运行起来了,那就可以注册设备、中断入口、中断服务函数了。接下来的事情就好办了

    搞清出设备如何被加载以后,我们还需要知道另外一个问题:怎样把一个模块的驱动程序加载到内核里面呢?SO简单,make menuconfig,把对应设备打开。但是能不能再具体一点呢,我们做这么一个改动,怎么映射到编译&链接过程呢。我这个人就是喜欢找麻烦,因此又在网上搜啊搜,而且用了最笨的方法,看看make menuconfig前后那些文件的修改日期发生了变化。最终还是找到了一点,/scripts下的文件是用来支持各种config模式的(当然包括 menuconfig),核心代码在Kconfig中。在每个驱动设备的文件夹下(比如net,mtd)都有一个叫config.in的文件,这些文件定义了我们在menuconfig画面中看到的目录结构&选项。

    眼睛看到的画面总归都是虚的,这些改动究竟反映到了哪里去了呢?两个文件:./config和/include/linux/ autoconf.h。我们做完menuconfig以后,所有改动就反映到了这两个文件中,这两个文件的内容是一致的。在我们做编译的过程中,顶层的 makefile文件从autoconf.h文件中读取各项宏定义然后传递给子一层的makefile,这些makefile根据宏定义选择那些.o文件被链接进来加到内核中。

    好了,知道这些我就知道怎么给8019添加驱动了,yy一下:

    1,首先要有驱动程序代码,8019.c

    2,修改net目录下的config.in文件中添加一项,

     dep_tristate '    RTL8019 support' CONFIG_RTL8019 $CONFIG_ISA

    3,打开menuconfig,将RTL8019 support选择y,保存退出后autoconf文件中应该就有了一个宏定义:#define CONFIG_RTL8019

    4,打开net目录下的makefile,添加:
     obj-$( CONFIG_RTL8019) += 8019.o

    5,make dep; make zImage;搞定!


    注:在menuconfig中选择m和 y的区别:
    y: 模块驱动编译到内核中,启动时自动加载

    m:模块会被编译,但是不会被编译到内核中,只是生成.o文件,我们可以收集这些.o文件做到linux的文件系统中,然后用insmod实现动态加载。

http://hi.baidu.com/jackyu/blog/item/a89ba634e31418bcd1a2d3e5.html

2010-12-07-21-55
分享到:
评论

相关推荐

    nginx+Linux版本离线依赖安装包(亲测可用),zlib,gcc,perl

    在IT领域,尤其是在服务器管理和系统部署的过程中,经常需要在没有互联网连接或者网络环境不稳定的情况下进行软件的安装。这时,离线依赖安装包就显得尤为重要。本文将详细介绍如何使用标题为"nginx+Linux版本离线...

    创建嵌入式系统开发环境.pptx

    Linux常用的命令行工具如vi编辑器和gcc编译器是开发过程中必不可少的。为了在非Linux平台上(如PC)编译适用于嵌入式硬件的代码,需要构建交叉编译工具链。 ARM处理器和Thumb指令集是嵌入式系统中的常见处理器架构...

    zz-网络安全试题(2).docx

    在Linux环境中,使用openssl创建自签名证书和私钥,以实现仅允许通过SSL加密访问的设置,并在httpd.conf中加载相关模块。 A-4 任务四:事件监控 调整Windows的安全日志文件大小至至少128MB,并设定当日志达到上限...

    pythonopencv源码DNN模块

    这一模块提供了加载预训练的深度神经网络模型的功能,并能够利用这些模型进行图像分类、目标检测等多种计算机视觉任务。DNN模块支持多种流行的深度学习框架下的模型,比如TensorFlow、Caffe、DarkNet等。 #### 三、...

    Linux的一些概念

    这种灵活性得益于其模块化的内核设计,使得Linux能够轻松适应各种硬件配置,同时保持对上层应用程序的透明性。 Linux的文件系统采用了与Windows不同的组织方式。在Linux中,整个文件系统构成一棵树形结构,所有文件...

    bigfatwifi:在一个模块中集合了您最喜欢的wifi驱动程序

    在Linux内核开发中,"zz"通常用于表示文件或目录的排序位置,意味着bigfatmod可能被设计为加载到内核的最后,这可能是为了确保其他驱动程序的正常工作,或者是因为该模块具有特殊的依赖关系。这种模块化设计使得用户...

    2024嵌入式面试资料uboot基础

    U-Boot(Universal Boot Loader)是一种广泛使用的开放源代码启动加载程序,尤其适用于嵌入式Linux系统。它不仅支持多种处理器架构,还具备高度可移植性、灵活性等特点,因此在嵌入式领域有着举足轻重的地位。 ### ...

    嵌入式 QT5 软键盘 支持中英文

    在Linux系统中,这通常通过QT的包管理器或者源码编译来完成。对于Windows,可以通过QT Creator的集成开发环境进行添加。 接着,你需要创建一个新的QT项目,并在项目中引入`QVirtualKeyboard`。在`main.cpp`文件中,...

    USB转串口驱动

    而在Linux系统中,内核通常已经包含了对许多USB转串口芯片的支持,只需加载相应的模块即可。对于MacOS,系统可能需要额外的驱动软件,如FtdiSio驱动,以便识别和使用这些设备。 为了确保设备的稳定运行,保持驱动...

    publish.zip

    《ASP.NET Core在CentOS 7上...值得注意的是,实际部署过程中可能会遇到各种问题,如依赖缺失、权限设置不当等,都需要逐一排查解决。同时,为了确保系统的安全性和稳定性,建议定期更新软件,保持系统补丁的最新状态。

    OPENCV目标跟踪LatentSVM算法实现行人检测opencv

    8. OpenCV中的实现:通过OpenCV中的函数和模块加载训练好的模型并进行行人检测。 9. 资料获取:可能通过百度网盘的分享地址获取预训练模型和相关资料。 以上就是从标题、描述、标签和部分内容中提取出的相关知识点...

    内存取证工具

    综上所述,"DumpIt"和"Volatility Framework"是内存取证过程中不可或缺的工具,它们的结合使用可以帮助网络安全专家深入挖掘系统内存中的信息,揭示潜在的威胁和犯罪行为。通过熟练掌握这些工具的使用,可以在应对...

    py源码实例实例wxpy获取微信好友头像

    ### py源码实例:使用wxpy获取微信好友头像 #### 概述 在本篇文章中,我们将深入探讨如何利用Python编程...当然,在实际操作过程中还需要注意各种细节问题,如网络状况、权限限制等,确保应用程序的稳定性和合规性。

    pythonopencv源码人脸关键点定位

    4. **人脸检测**:利用OpenCV中的`cv2.CascadeClassifier`类加载级联分类器文件,进行人脸区域的检测。 5. **关键点定位**:根据选择的方法调用相应函数进行关键点定位。 ```python # 使用Dlib进行关键点定位 ...

Global site tag (gtag.js) - Google Analytics