内核启动时,设备及驱动初始化的实现

转载 2012年03月30日 10:24:22

转自:http://www.cnitblog.com/vsolo/archive/2008/04/21/42592.html

Uboot完成系统的引导并将Linux内核拷贝到内存之后,bootm -> do_bootm_linux()跳转到kernel的起始位置;
       压缩过的kernel入口在arch/arm/boot/compressed/head.S,它将调用函数decompress_kernel()<./arch/arm/boot/compressed/misc.c>解压,打印“Uncompressing Linux...”,调用gunzip(),打印"done, booting the kernel."
       然后call_kernel,执行解压后的kernel,经linux/arch/arm/kernel/head.S调用start_kernel转入体系结构无关的通用C代码,在start_kernel()中完成了一系列系统初始化,设备及驱动的注册即在此时完成:

<./init/main.c>-------------------------

asmlinkage void __init start_kernel(void)
{
 char * command_line;
 extern struct kernel_param __start___param[], __stop___param[];

···········································································
 printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);
                                                          //打印内核命令行
 parse_early_param();
 parse_args("Booting kernel", command_line, __start___param,
     __stop___param - __start___param,
     &unknown_bootoption);
                                                        //解析由BOOT传递的启动参数
···········································································

 /* Do the rest non-__init'ed, we're now alive */
 rest_init();
}

start_kernel()中的函数rest_init()将创建第一个核心线程kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND),调用init()函数:

static int init(void * unused)-------------------
{
                ·······················
                 do_basic_setup();
                ······················

 /*
  * We try each of these until one succeeds.
  *
  * The Bourne shell can be used instead of init if we are
  * trying to recover a really broken machine.
  */
 if (execute_command) { //判断在启动时是否指定了init参数
                                      //如果指定则执行用户init进程,成功将不会返回
  run_init_process(execute_command);
  printk(KERN_WARNING "Failed to execute %s.  Attempting "
     "defaults...\n", execute_command);
 }

               /*   如果没有指定init启动参数,则查找下面的目录init进程,成功将不会返回,否则打印出错信息   */
 run_init_process("/sbin/init");
 run_init_process("/etc/init");
 run_init_process("/bin/init");
 run_init_process("/bin/sh");

 panic("No init found.  Try passing init= option to kernel.");

}

继而调用函数do_basic_setup()(此时与体系结构相关的部分已经初始化完了,现在开始初始化设备了):

/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)-----------------
{
 /* drivers will send hotplug events */
 init_workqueues();
 usermodehelper_init();
 driver_init();     //建立设备模型子系统

#ifdef CONFIG_SYSCTL
 sysctl_init();
#endif

 /* Networking initialization needs a process context */
 sock_init();

 do_initcalls();   //系统初始化(包括设备,文件系统,内核模块等)
}
<./drivers/base/init.c>-------------------------
/**
 * driver_init - initialize driver model.
 *
 * Call the driver model init functions to initialize their
 * subsystems. Called early from init/main.c.
 */

void __init driver_init(void)
{
 /* These are the core pieces */
 devices_init();
                       <./drivers/base/core.c>-------------
                                  int __init devices_init(void)
                                  {
                   return subsystem_register(&devices_subsys);
                                  }
                        -----------------------
 buses_init();
 classes_init();
 firmware_init();

 /* These are also core pieces, but must come after the
  * core core pieces.
  */
 platform_bus_init();
 system_bus_init();
 cpu_dev_init();
 memory_dev_init();
 attribute_container_init();
}
---------------------------
extern initcall_t __initcall_start[], __initcall_end[];

static void __init do_initcalls(void)
{
 initcall_t *call;
 int count = preempt_count();

 for (call = __initcall_start; call < __initcall_end; call++) {
                ··················
                (*call)();     //调用一系列初始化函数
               ···················
}
---------------------------
      __initcall_start和__initcall_end界定了存放初始化函数指针区域的起始地址,即从__initcall_start开始到__initcall_end结束的区域中存放了指向各个初始化函数的函数指针。 由 (*call)()完成各个部分的初始化工作,且便于扩充。具体实现如下:
<./arch/arm/kernel/vmlinux.lds.S>-----------------
  __initcall_start = .;
   *(.initcall1.init)
   *(.initcall2.init)
   *(.initcall3.init)
   *(.initcall4.init)
   *(.initcall5.init)
   *(.initcall6.init)
   *(.initcall7.init)
  __initcall_end = .;

 <./include/linux/init.h>---------------------

#ifndef MODULE     /*    如果驱动模块静态编译进内核   */

  ···············································

/* initcalls are now grouped by functionality into separate
 * subsections. Ordering inside the subsections is determined
 * by link order.
 * For backwards compatibility, initcall() puts the call in
 * the device init subsection.
 */

#define __define_initcall(level,fn) \
 static initcall_t __initcall_##fn __attribute_used__ \
 __attribute__((__section__(".initcall" level ".init"))) = fn

#define core_initcall(fn)  __define_initcall("1",fn)
#define postcore_initcall(fn)  __define_initcall("2",fn)
#define arch_initcall(fn)  __define_initcall("3",fn)
                                            //此处初始化了设备
                                           /*----eg:arch_initcall(at91sam9261_device_init)---
                                               static int __init at91sam9261_device_init(void)
                                               {
                                                 at91_add_device_udc();
                                                 at91_add_device_dm9000();
                                                 armebs3_add_input_buttons();
                                                 return platform_add_devices(at91sam9261_devices, ARRAY_SIZE(at91sam9261_devices));
                                                }
                                        ------------------------*/

#define subsys_initcall(fn)  __define_initcall("4",fn)
#define fs_initcall(fn)  __define_initcall("5",fn)
#define device_initcall(fn)  __define_initcall("6",fn)
                                           //此处初始化了静态编译的驱动模块
#define late_initcall(fn)  __define_initcall("7",fn)

#define __initcall(fn) device_initcall(fn)


  /**
 * module_init() - driver initialization entry point
 * @x: function to be run at kernel boot time or module insertion
 *
 * module_init() will either be called during do_initcalls (if
 * builtin) or at module insertion time (if a module).  There can only
 * be one per module.
 */
#define module_init(x) __initcall(x);
                                       //静态编译的驱动模块作为device_initcall在内核启动就被do_initcalls

/**
 * module_exit() - driver exit entry point
 * @x: function to be run when driver is removed
 *
 * module_exit() will wrap the driver clean-up code
 * with cleanup_module() when used with rmmod when
 * the driver is a module.  If the driver is statically
 * compiled into the kernel, module_exit() has no effect.
 * There can only be one per module.
 */
#define module_exit(x) __exitcall(x);

#else /* MODULE    如果驱动模块动态加载入内核   */

  ···············································

/* Each module must use one module_init(), or one no_module_init */
#define module_init(initfn)     \
 static inline initcall_t __inittest(void)  \
 { return initfn; }     \
 int init_module(void) __attribute__((alias(#initfn)));
     //insmod 是通过系统调用sys_init_module(const char *name_user, struct module *mod_user)
     //将动态驱动模块载入到内核空间

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)     \
 static inline exitcall_t __exittest(void)  \
 { return exitfn; }     \
 void cleanup_module(void) __attribute__((alias(#exitfn)));

-----------------------------

相关文章推荐

内核启动时,设备及驱动初始化的实现

内核启动时,设备及驱动初始化的实现   Uboot完成系统的引导并将Linux内核拷贝到内存之后,bootm -> do_bootm_linux()跳转到kernel的起始位置;       压缩过的...

内核启动时,设备及驱动初始化的实现

Uboot完成系统的引导并将Linux内核拷贝到内存之后,bootm -> do_bootm_linux()跳转到kernel的起始位置;        压缩过的kernel入口在arch/...

内核启动时,设备及驱动初始化的实现

本文完全转载,地址:http://www.cnitblog.com/vsolo/archive/2008/04/21/42592.aspx,版权问题,马上删除。作者的博客写的很用心,文章很到位透彻。 ...

内核启动时,设备及驱动初始化的实现

内核启动时,设备及驱动初始化的实现

嵌入式linux 内核启动时动态加载驱动模块的方法

前言手头有个项目底层基本结束,所有的驱动都已经写好,并且调试通过了,但是考虑到每次使用时都手动加载驱动,岂不是很“蠢”,能自动的事怎么能手动呢? 于是开始折腾“linux开机自动加载驱动”,百度,谷...

ARM linux内核启动时几个关键地址

1.       内核启动地址 1.1.   名词解释 ZTEXTADDR 解压代码运行的开始地址。没有物理地址和虚拟地址之分,因为此时MMU处于关闭状态。这个地址不一定时RAM的地址,可以是支...
  • sfrysh
  • sfrysh
  • 2011-11-06 12:31
  • 1741

linux内核启动时,第一次创建页表

uboot启动linux内核启动时,linux内核页表的创建

yaffs2 文件系统 内核启动时 Failed to execute /linuxrc. 问题的解决

在用制作的yaffs2 文件系统启动内核遇到下面的问题: Warning: unable to open an initial console. Failed to execute /linuxrc....

查看Linux内核启动时调用的init函数

Linux内核引导加载后 会调用一些初始化函数 其中有很多模块化的代码使用do_initcalls调用 这些函数通过宏pure_initcall、core_initcall、subsys_ini...

内核启动时怎么处理启动参数

转自: http://blog.csdn.net/zhenwenxian/article/details/5988729 Linux允许用户通过bootloader传递内核配置选项给内核,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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