linux kernel_init

来源:互联网 发布:淘宝情趣用品买家秀 编辑:程序博客网 时间:2024/06/03 03:58

前言:


内核在启动用户空间程序时会创建两个线程,kthread() 和 kernel_init()线程,在前一篇介绍了kthread()线程 点击打开链接,本文不在赘述,这里主要是对kernel_init()线程创建init=1号进程,并完成驱动模块的注册(这里的驱动模块是编译进内核的模块)。


1. kthreadd()

路径:linux-3.10.x\init\main.c

static noinline void __init_refok rest_init(void){int pid;rcu_scheduler_starting();/* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //init=1 号进程的创建numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //内核进程kthread的创建,用来管理内核层线程rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();complete(&kthreadd_done); //唤醒完成量/* * The boot idle thread must execute schedule() * at least once to get things moving: */init_idle_bootup_task(current);schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);}


2. kernel_init()

 路径:linux-3.10.x\init\main.c

功能:init=1号进程创建、内核驱动模块注册、启动默认控制台/dev/console

static int __ref kernel_init(void *unused){//kernel_init_freeable内部使用了等待完成量“kthreadd_done”,通过内核启动的报文我们得知,该线程虽然先创建//但却是在“kthreadd”线程创建完才执行的,原因是kthreadd执行完后才唤醒完成量的,所以这里会等待完成量!!!kernel_init_freeable();/* need to finish all async __init code before freeing the memory */async_synchronize_full();free_initmem(); //释放init初始化数据段,见下面mark_rodata_ro(); //标记只读数据段为只读,arm平台不影响system_state = SYSTEM_RUNNING;//设置系统为运行状态numa_default_policy(); //numa即非一致性内存访问,设置默认策略flush_delayed_fput();/*内核启动信息“Kernel command line: root=/dev/mtdblock4 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0,115200n8 rdinit=/sbin/init ro mem=64M”如果ramdisk_execute_command变量指定了要运行的程序,启动它。  ramdisk_execute_command的取值分为三种情况:  a.如果命令行参数中指定了“rdinit=...”,则ramdisk_execute_command等于这个参数指定的程序。  b.否则,如果/init程序存在,ramdisk_execute_command就等于/init  c.否则,ramdisk_execute_command为空  韦老师书中所用的命令没有设定“rdinit=...”,根文件系统中也没有"/init"。所以ramdisk_execute_command为空。if下的那块程序不会执行。*/if (ramdisk_execute_command) {if (!run_init_process(ramdisk_execute_command))return 0;pr_err("Failed to execute %s\n", ramdisk_execute_command);}/* * 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) {if (!run_init_process(execute_command))return 0;pr_err("Failed to execute %s.  Attempting defaults...\n",execute_command);}/*分析文件系统中init源码,里面主要是1号进程创建,环境设置,还有文件系统的console,*/if (!run_init_process("/sbin/init") ||    !run_init_process("/etc/init") ||    !run_init_process("/bin/init") ||    !run_init_process("/bin/sh"))return 0;panic("No init found.  Try passing init= option to kernel. "      "See Linux Documentation/init.txt for guidance.");}

void free_initmem(void){#ifdef CONFIG_HAVE_TCMextern char __tcm_start, __tcm_end;poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start);free_reserved_area(&__tcm_start, &__tcm_end, 0, "TCM link");#endifpoison_init_mem(__init_begin, __init_end - __init_begin); //释放init段if (!machine_is_integrator() && !machine_is_cintegrator())free_initmem_default(0);}
/* * Poison init memory with an undefined instruction (ARM) or a branch to an * undefined instruction (Thumb). */static inline void poison_init_mem(void *s, size_t count){u32 *p = (u32 *)s;for (; count != 0; count -= 4)*p++ = 0xe7fddef0;/*内核初始化时把这些内存区域初始化为0xe7fddef0 (an undefined instruction (ARM) or a branch to an undefined instruction (Thumb)),如果运行时函数非法访问到了这些区域,会触发一个undef instruction的异常并打印相应的回调,从而辅助开发人员更快地解决相关问题。*/}


3. kernel_init_freeable()

路径:linux-3.10.x\init\main.c

功能:kernel_init_freeable主要功能是,等待内核线程创建完wait_for_completion(&kthreadd_done)、注册内核驱动模块do_basic_setup()、启动默认控制台/dev/console

static noinline void __init kernel_init_freeable(void){/* * Wait until kthreadd is all set-up. */wait_for_completion(&kthreadd_done); //等待完成量,即等待kthreadd线程完成/* Now the scheduler is fully set up and can do blocking allocations */gfp_allowed_mask = __GFP_BITS_MASK;/* * init can allocate pages on any node */set_mems_allowed(node_states[N_MEMORY]);/* * init can run on any cpu. */set_cpus_allowed_ptr(current, cpu_all_mask);cad_pid = task_pid(current);smp_prepare_cpus(setup_max_cpus);do_pre_smp_initcalls();lockup_detector_init();smp_init();sched_init_smp();do_basic_setup();/* Open the /dev/console on the rootfs, this should never fail */if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)pr_err("Warning: unable to open an initial console.\n");(void) sys_dup(0);(void) sys_dup(0);/* * check if there is an early userspace init.  If yes, let it do all * the work */if (!ramdisk_execute_command)ramdisk_execute_command = "/init";if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {ramdisk_execute_command = NULL;prepare_namespace();}/* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. *//* rootfs is available now, try loading default modules */load_default_modules();}


4. do_basic_setup()

路径:linux-3.10.x\init\main.c

功能:初始化cpuset子系统、创建khelper线程队列、内核模块驱动注册、

static void __init do_basic_setup(void){cpuset_init_smp(); //初始化内核control group的cpuset子系统usermodehelper_init();//创建khelper单线程工作队列,用于协助新建和运行用户空间程序shmem_init(); //初始化共享内存driver_init(); //驱动模块总线、文件系统注册,包括bus、devtmpfs、platforminit_irq_proc(); //创建/proc/irq目录, 并初始化系统中所有中断对应的子目录do_ctors(); //执行内核的构造函数usermodehelper_enable(); //使能usermodehelperdo_initcalls(); //调用level 0到level 7的initcall函数,依次的level名称是"early", "core", "postcore", "arch", "subsys", "fs", "device", “late”,需要注意的kernel在这块的命名有些问题,early_initcall对应的level小于0,pure_initcall对应level才是0random_int_secret_init(); //初始化随机数生成池}

void __init driver_init(void){/* These are the core pieces */devtmpfs_init();devices_init();buses_init();classes_init();firmware_init();hypervisor_init();/* These are also core pieces, but must come after the * core core pieces. */platform_bus_init(); //见此文分析:http://blog.csdn.net/xichangbao/article/details/52938240cpu_dev_init();memory_dev_init();}


5. do_initcalls()
路径:linux-3.10.x\init\main.c

功能:初始化initcall_levels,完成编译进内核的驱动模块注册

static void __init do_initcalls(void){int level;for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)do_initcall_level(level);}
由于这里涉及到的东西比较多,可以见之前写的一篇博客点击打开链接,本文不再赘述!

至此,内核的初始化结束,正式进入了用户空间的初始化过程至此,内核的初始化结束,正式进入了用户空间的初始化过程至此,内核的初始化结束,正式进入了用户空间的初始化过程,在kernel_init线程中调用的do_basic_setup()函数会去初始化设备驱动,完成其他驱动程序(直接编译进内核的模块)的初始化。内核中大部分的启动数据输出(都是各设备的驱动模块输出)都是这里产生的。是我们驱动工程师需要重点关注的函数。