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中。在每个驱动设备的文件夹下(比如netmtd)都有一个叫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实现动态加载。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
精易模块V7.5.0 what's new:(20190701) 一、新增 1、新增“rar_测试文件”测试指定压缩包指定密码是否正确;感谢【@rszhao 】建议; 2、新增“文件_读入字节集”API方式读入文件; 3、新增“类_列队 - 取数量”取出当前列队数量;感谢【@随机破解 】反馈; 4、新增“类_树型框_字节集操作”仅作为树型框项目数据的操作,并非对外部或自身组件操作; 5、新增“时间_转为GMT格式3”将日期时间型数据转换为GMT格式 Greenwich Mean Time;感谢【@xdm1957 】建议; 6、新增“窗口_重画”这个函数屏蔽一个窗口客户区的全部或部分区域。这会导致窗口在事件期间部分重画  非零表示成功,零表示失败; 7、新增“BASE64编码_ASM”“BASE64解码_ASM”“取文本长度_ASM”高性能汇编版base64编码与解码; 8、新增“外部树型框 - 查找项目_外部”快速查找外部树型框指定关键词相关项目; 9、新增“窗口_是否响应”检查目标窗口是否及时响应; 10、新增“窗口_隐藏任务按钮”顶级窗口,会在任务条里显示一个按钮.通过本功能可以将其隐藏; 11、新增"数组_去重复_整数型"提升执行效率。感谢【@Mr.Yang 】建议; 二、修复 1、修复“外部编辑框_置左右边距”参数赋值错误的BUG; 2、修复“图片_缩放_透明”内存泄漏问题;感谢【@yinhezeyu 】反馈【@流星暴雨 】建议; 3、修复“文件_搜索1”返回数组没有子文件夹信息的BUG;感谢【@wg521125 】反馈; 4、修复“网页_取外网IP”失效的问题;感谢【@xinhan2012 】反馈; 5、修复“文本_到小写EX”传入参数被修改的BUG; 6、修复“托盘类 - 气泡提示”显示时间异常的BUG;感谢【@留意 】反馈; 7、修复“图片_平铺拉伸”未公开的问题; 三、优化 1、优化“运算_颜色转换”的备注,添加互转命令提示; 2、优化“文本_转拼音”添加“是否正则”参数,使命令更灵活;感谢【@ab63333334 】建议; 3、优化“文本_朗读”“文本_朗读Ex”参数“朗读方式” 真=支持库方式  假=API方式  默认为假  API方式;感谢【@jixun66 】反馈; 4、优化“取错误信息文本”为“取错误信息文本_API”,避免与支持库命令冲突的问题;感谢【@970767701 】反馈; 5、优化“网页_访问S”“网页_访问”忽略证书的功能;感谢【@Love苍井空 】反馈; 6、优化“外部树型框”整个类,加入更多实用命令; 四、移除 1、移除“系统_取声卡名称”,理由:waveOutGetDevCapsA的自定义数据名称成员为固定长度的数据,导致取出的播放设备名不全。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值