详解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 = .;

相关推荐