-----以下内容为从网络上整理所得------

主要介绍kernel_init线程(函数),这个线程在rest_init函数中被创建,kernel_init函数将完成设备驱动程序的初始化,并调用init_post函数启动用户空间的init进程。

static int __init kernel_init(void * unused)
{
lock_kernel();
  //锁住内核
set_mems_allowed(node_states[N_HIGH_MEMORY]);
  //init可以在任何节点(node)分配到内存页
set_cpus_allowed_ptr(current, cpu_all_mask);
  //init可以在任何CPU上运行.
init_pid_ns.child_reaper = current;
//把当前进程设为接受其他孤儿进程的进程 cad_pid = task_pid(current); /*SMP系统做准备,激活所有CPU,并开始SMP系统的调度*/
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
start_boot_trace();
smp_init();
sched_init_smp(); do_basic_setup();
//驱动程序和内核子系统的一般初始化,下面会详解 //检查是否有早期用户空间的init程序。如果有,让其执行
if (!ramdisk_execute_command)ramdidk_execute_command="/init";
if (sys_access((const char __user *) ramdisk_execute_command, ) != )
{
ramdisk_execute_command = NULL;
prepare_namespace();
}
//最后调用init_post,启动进程负责用户空间的初始化
init_post();
return ;
}

在内核init线程的最后执行了init_post函数,在这个函数中真正启动了用户空间进程init,详解如下:

static noinline int init_post(void)
__releases(kernel_lock)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
free_initmem();
unlock_kernel(); mark_rodata_ro();
//通过修改页表,保证只读数据段为只读属性。大部分构架为空函数。 system_state = SYSTEM_RUNNING;
//设置系统状态为运行状态 numa_default_policy();
//设定NUMA系统的内存访问策略为默认 //打开根文件系统中的 /dev/console , 此处不可失败
if (sys_open((const char __user *) "/dev/console", O_RDWR, ) < )
printk(KERN_WARNING "Warning: unable to open an initial console.\n"); /*复制两次标准输入(0)的文件描述符
*它是上面打开的/dev/console,也就是系统控制台):
*一个作为标准输出(1)
*一个作为标准出错(2)
*现在标准输入、标准输出、标准出错都是/dev/console了。
*这个console在内核启动参数中可以配置为某个串口(ttySn、ttyOn等等),
*也可以是虚拟控制台(tty0)。
*所以我们就在串口或者显示器上看到了之后的系统登录提示。
**/
(void) sys_dup();
(void) sys_dup(); //设置当前进程(init)为不可以杀进程(忽略致命的信号)
current->signal->flags |= SIGNAL_UNKILLABLE; //如果ramdisk_execute_command有指定的init程序,就执行它。
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
//如果execute_command有指定的init程序,就执行它。
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
/*在检查完ramdisk_execute_command和execute_command为空的情况下,
*顺序执行以下初始化程序:如果都没有找到就打印错误信息。
*这也是我们做系统移植的时候经常碰到的错误信息,出现这个信息很有可能是:
*1、你的启动参数配置有问题,通过 指定了init程序,但是没有找到,
* 且默认的那四个程序也不在文件系统中。
*2、文件系统挂载有问题,文件不存在
*3、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。在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化工作,详解如下:

/*
* 好了, 设备现在已经初始化完成。 但是还没有一个设备被初始化过,
* 但是 CPU 的子系统已经启动并运行,
* 且内存和处理器管理系统已经在工作了。
*
* 现在我们终于可以开始做一些实际的工作了..
*/
static void __init do_basic_setup(void)
{
rcu_init_sched(); /* needed by module_init stage. */
init_workqueues(); //初始化工作队列 /*针对SMP系统,初始化内核control group的cpuset子系统。
*如果非SMP,此函数为空。
*cpuset是在用户空间中操作cgroup文件系统来执行进程
*与cpu和进程与内存结点之间的绑定。
*本函数将cpus_allowed和mems_allwed更新为在线的cpu和在线的内存结点,
*并为内存热插拨注册了钩子函数,最后创建一个单线程工作队列cpuset。
*/
cpuset_init_smp(); /*创建一个单线程工作队列khelper。
*运行的系统中只有一个,主要作用是指定用户空间的程序路径和环境变量,
*最终运行指定的user space的程序,属于关键线程,不能关闭。
*/
usermodehelper_init(); //初始化驱动模型中的各子系统,可见的现象是在/sys中出现的目录和文件
driver_init(); //在proc文件系统中创建irq目录,并在其中初始化系统中所有中断对应的目录。
init_irq_proc(); /*调用链接到内核中的所有构造函数,也就是链接进.ctors段中的所有函数。
*在内核启动和模块挂载时,调用构造函数(gcc生成的类初始化函数)。
*构造函数就是比如用于初始化gcov数据的函数
*/
do_ctors(); /*调用所有编译内核的驱动模块中的初始化函数。
*这里就是驱动程序员需要关心的步骤,其中按照各个内核模块初始化函数
*所自定义的启动级别(1~7),按顺序调用器初始化函数。
*对于同一级别的初始化函数,安装编译是链接的顺序调用,
*也就是和内核Makefile的编写有关。
*
*在编写内核模块的时候需要知道这方面的知识,比如你编写的模块使用
*的是I2C的API,那你的模块的初始化函数的级别必须低于I2C子系统
*初始化函数的级别(也就是级别数(1~7)要大于I2C子系统)。
*如果编写的模块必须和依赖的模块在同一级,那就必须注意内核Makefile的修改了。
*/
do_initcalls();
}

上面的函数调用了driver_init函数,作用是驱动模型子系统的初始化,对于内核驱动工程师来说比较重要,详解如下:

/**
* driver_init - 初始化驱动模型.
*
* 调用驱动模型初始化函数来初始化它们的子系统。
* 由早期的init/main.c中调用。
*/
void __init driver_init(void)
{
/* These are the core pieces */
/*初始化驱动模型中的部分子系统和kobject:
*devices
*dev
*dev/block
*dev/char
*/
devices_init();
buses_init();//初始化驱动模型中的bus子系统
classes_init();//初始化驱动模型中的class子系统
firmware_init();//初始化驱动模型中的firmware子系统
hypervisor_init();//初始化驱动模型中的hypervisor子系统 /* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();//初始化驱动模型中的bus/platform子系统
system_bus_init();//初始化驱动模型中的devices/system子系统
cpu_dev_init();//初始化驱动模型中的devices/system/cpu子系统
memory_dev_init();//初始化驱动模型中的devices/system/memory子系统
}

linux内核启动分析(2)的更多相关文章

  1. Linux内核启动分析过程-《Linux内核分析》week3作业

    环境搭建 环境的搭建参考课件,主要就是编译内核源码和生成镜像 start_kernel 从start_kernel开始,才真正进入了Linux内核的启动过程.我们可以把start_kernel看做平时 ...

  2. Linux内核启动分析

    张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我的代码可见https://www.shiyanlo ...

  3. Linux内核启动分析笔记

    一.驱动加载 1.驱动加载调用关系 start_kernel //init/main.c rest_init //最后执行它 kernel_init //使用kernel_thread创建一个进程执行 ...

  4. linux内核启动分析(3)

    主要分析do_basic_setup函数里面的do_initcalls()函数,这个函数用来调用所有编译内核的驱动模块中的初始化函数. static void __init do_initcalls( ...

  5. 通过从代码层面分析Linux内核启动来探知操作系统的启动过程

    通过从代码层面分析Linux内核启动来探知操作系统的启动过程 前言说明 本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernel到init进程 ...

  6. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  7. Linux内核及分析 第三周 Linux内核的启动过程

    实验过程: 打开shell终端,执行以下命令: cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage-initrd rootf ...

  8. Linux内核启动流程分析(一)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380535.html 很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下.由于是word直接 ...

  9. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

随机推荐

  1. (沒有介紹標準算法的)RMQ問題

    感謝杜哥代碼滋磁 //以下是廢話 RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中 ...

  2. Hadoop运行Jar文件时Output错误

    当第二次运行Jar程序时,出现Output文件已存在的Exception: Exception in thread "main" org.apache.hadoop.mapred. ...

  3. VS2017常用快快捷键

    VS2017常用快快捷键 VS中代码对齐等快捷键        在VS中,选定代码后,按Ctrl+K+F组合键,可以自动进行代码对齐.        注意:要先按下Ctrl和K,再按下F,因为Ctrl ...

  4. UVA.129 Krypton Factor (搜索+暴力)

    UVA.129 Krypton Factor (搜索+暴力) 题意分析 搜索的策略是:优先找长串,若长串不合法,则回溯,继续找到合法串,直到找到所求合法串的编号,输出即可. 注意的地方就是合法串的判断 ...

  5. bzoj1010: [HNOI2008]玩具装箱toy(斜率优化DP)

    Orz CYC帮我纠正了个错误.斜率优化并不需要决策单调性,只需要斜率式右边的式子单调就可以了 codevs也有这题,伪·双倍经验233 首先朴素DP方程很容易看出:f[i]=min(f[j]+(i- ...

  6. NOIP2016Day1T2天天爱跑步(LCA+桶)

    据说是今年NOIP最难一题了...我还记得当时满怀期待心情点开Day1的题发现T2就不会了于是怀疑人生良久... 啊好像很多大爷都是用线段树合并写的,我怎么什么数据结构都不会啊呜呜呜... 题目大意就 ...

  7. ucenter通信实现同步登录、同步退出(详细)

    首先,需要去官网下载一个ucenter的包.然后解压下来. 先把ucenter/ucenter这个文件夹复制到你的项目根目录下改名为uc_server;(这里只是我建议修改,以便于我下面写的配置); ...

  8. 牛客326B 背单词

    传送门:https://ac.nowcoder.com/acm/contest/326/B 题意:给你一个n,你需有找有多少个长度从1~n 的单词,满足最长连续元音少于A个并且最长连续辅音长度少于B. ...

  9. Java的位运算符—与(&)、非(~)、或(|)、异或(^)

    位运算符主要针对二进制,它包括了:“与”.“非”.“或”.“异或”.从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系运算符来进行逻辑运算,而位运算符主要针对两个二进制数的位进行逻辑运算.下面 ...

  10. Tomcat 映射虚拟目录和程序热部署

    虚拟目录的设置 方法一:在${tomcat安装目录}/conf/Catalina/localhost目录下创建一个xml文件,任意文件名都可以,但是此文件名是web应用发布后的虚拟目录: 比如创建一个 ...