最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

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

工作和技术 crifan 1608浏览 0评论

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

参见include/linux/init.h和vmlinux.lds

1)
所有标识为__init的函数在链接的时候都放在.init.text这个区段内,
在这个区段中,函数的摆放顺序是和链接的顺序有关的,是不确定的。

2)
所有的__init函数在区段.initcall.init中还保存了一份函数指针,
在初始化时内核会通过这些函数指针调用这些__init函数指针,
并在整个初始化完成后,释放整个init区段(包括.init.text,.initcall.init等),

注意,这些函数在内核初始化过程中的调用顺序只和这里的函数指针的顺序有关,
和1)中所述的这些函数本身在.init.text区段中的顺序无关。

在2.4内核中,这些函数指针的顺序也是和链接的顺序有关的,是不确定的。

在2.6内核中,initcall.init区段又分成7个子区段,分别是
.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init

当需要把函数fn放到.initcall1.init区段时,只要声明
core_initcall(fn);
即可。

其他的各个区段的定义方法分别是:
core_initcall(fn) —>.initcall1.init
postcore_initcall(fn) —>.initcall2.init
arch_initcall(fn) —>.initcall3.init
subsys_initcall(fn) —>.initcall4.init
fs_initcall(fn) —>.initcall5.init
device_initcall(fn) —>.initcall6.init
late_initcall(fn) —>.initcall7.init

而与2.4兼容的initcall(fn)则等价于device_initcall(fn)。

各个子区段之间的顺序是确定的,即先调用.initcall1.init中的函数指针
再调用.initcall2.init中的函数指针,等等。
而在每个子区段中的函数指针的顺序是和链接顺序相关的,是不确定的。

在内核中,不同的init函数被放在不同的子区段中,因此也就决定了它们的调用顺序。
这样也就解决了一些init函数之间必须保证一定的调用顺序的问题。

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: %sn", 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)));

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

转载请注明:在路上 » 【转】内核启动时,设备及驱动初始化的实现

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.236 seconds, using 22.19MB memory