Chinaunix首页 | 论坛 | 博客
  • 博客访问: 616910
  • 博文数量: 140
  • 博客积分: 2635
  • 博客等级: 少校
  • 技术积分: 1353
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-04 15:46
文章分类
文章存档

2015年(2)

2014年(12)

2013年(10)

2012年(10)

2011年(85)

2010年(21)

分类: LINUX

2011-04-11 09:53:21

如果后面为unused,则表示该模块当前没在使用。如果后面有autoclean,则该模块可以被rmmod -a命令自动清洗。rmmod -a命令会将目前有autoclean的模块卸载,如果这时候某个模块未被使用,则将该模块标记为autoclean。如果在行尾的[ ]括号内有模块名称,则括号内的模块就依赖于该模块。例如:

cdrom 34144 0 [sr_mod ide-cd]

其中ide-cd及sr_mod模块就依赖于cdrom模块。

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;搞定!


阅读(4136) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~