Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】
前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化。在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线程init。这个函数虽然意思为剩下的初始化,但是这个“剩下”的可是内容颇多,下面详细分析如下:
- /*
- * 我们必须确定在一个非__init函数或
- * 其他根线程(root thread)和初始化线程(init thread)间的竞态。
- * (这种竞态可能导致start_kernel在根线程运作到cpu_idle前被free_initmem“收割”。)
- *
- *
- * gcc-3.4 偶尔会将这个函数作为内联函数, 所以使用了noinline.
- */
- static __initdata DECLARE_COMPLETION(kthreadd_done);
- 定义一个complete变量来告诉init线程:kthreads线程已经创建完成。
- 从前似乎不是用complete锁,而是用大内核锁。
- static noinline void __init_refok rest_init(void)
- {
- int pid;
- rcu_scheduler_starting();
- /*
- * 我们必须先创建init内核线程,这样它就可以获得pid为1。
- * 尽管如此init线程将会挂起来等待创建kthreads线程。
- * 如果我们在创建kthreadd线程前调度它,就将会出现OOPS。
- */
- kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
- 创建kernel_init内核线程,内核的1号进程!!!!!
- numa_default_policy();
- 设定NUMA系统的内存访问策略为默认
- pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
- 创建kthreadd内核线程,它的作用是管理和调度其它内核线程。
- 它循环运行一个叫做kthreadd的函数,该函数的作用是运行kthread_create_list全局链表中维护的内核线程。
- 调用kthread_create创建一个kthread,它会被加入到kthread_create_list 链表中;
- 被执行过的kthread会从kthread_create_list链表中删除;
- 且kthreadd会不断调用scheduler函数让出CPU。此线程不可关闭。
上面两个线程就是我们平时在Linux系统中用ps命令看到:- tekkaman@Super-MAGI:~/development/analyze/linux-3.0$ ps -A
- PID TTY TIME CMD
- 1 ? 00:00:00 init
- 2 ? 00:00:00 kthreadd
- ......
rcu_read_lock();
- kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
- rcu_read_unlock();
- complete(&kthreadd_done);
- /*
- * 为让系统运作起来,
- * boot idle线程必须至少执行一次schedule():
- */
- init_idle_bootup_task(current);
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- /* 在抢占禁用时调用cpu_idle */
- cpu_idle();
- 此时内核本体进入了idle状态,用循环消耗空闲的CPU时间片,该函数从不返回。在有其他进程需要工作的时候,该函数就会被抢占!这个函数因构架不同而异。
- }
- 下面这个函数就是内核init线程运行的函数,它将完成设备驱动程序的初始化,并调用init_post函数启动用户空间的init进程。
- static int __init kernel_init(void * unused)
- {
- /*
- * 等待kthreadd的启动完成.
- */
- wait_for_completion(&kthreadd_done);
- /*
- * init可以在任何节点(node)分配到内存页
- */
- set_mems_allowed(node_states[N_HIGH_MEMORY]);
- /*
- * init可以在任何CPU上运行.
- */
- set_cpus_allowed_ptr(current, cpu_all_mask);
- 增加当前进程的CPU亲和力,使所有的CPU(如果是SMP)都可以运行本线程。
- 线程可以被迁移到被设置掩码的CPU上运行,但如果在位掩码中删除该CPU位,此线程就不会在那个CPU上运行。
- cad_pid = task_pid(current);
- cad_pid为接收Ctrl-alt-del操作的INT信号的进程ID,此处很明显是设为了init的PID
- smp_prepare_cpus(setup_max_cpus);
- do_pre_smp_initcalls();
- lockup_detector_init();
- smp_init();
- sched_init_smp();
- 以上代码是在SMP系统做准备,激活所有CPU,并开始SMP系统的调度。
- do_basic_setup();
- 到此,与构架相关的部分已经初始化完成了,do_basic_setup函数主要是初始化设备驱动,完成其他驱动程序(直接编译进内核的模块)的初始化。内核中大部分的启动数据输出(都是各设备的驱动模块输出)都是这里产生的。
- 此函数比较重要,以后会详细分析!
- /* 打开根文件系统中的 /dev/console , 此处不可失败 */
- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
- printk(KERN_WARNING "Warning: unable to open an initial console.\n");
- 这是kernel_init(以后的init进程)打开的第一个文件,它也就成为了标准输入。
- 这里需要打开 /dev/console,如果没有这个节点,系统就出错。这个错误信息也是经常碰到的。可能的原因是:
- 1、制作文件系统的时候忘记创建/dev/console节点
- 2、文件系统挂载问题,挂载上的文件系统不是什么都没有,就是挂错了节点。
- (void) sys_dup(0);
- (void) sys_dup(0);
- 复制两次标准输入(0)的文件描述符(它是上面打开的/dev/console,也就是系统控制台):
- 一个作为标准输出(1)
- 一个作为标准出错(2)
- 现在标准输入、标准输出、标准出错都是/dev/console了。
- 这个console在内核启动参数中可以配置为某个串口(ttySn、ttyOn等等),也可以是虚拟控制台(tty0)。所以我们就在串口或者显示器上看到了之后的系统登录提示。
- /*
- * 检查是否有早期用户空间的init程序。如果有,让其执行
- *
- */
- 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, 我们已经完成了启动初始化, and
- * 且我们本质上已经在运行。释放初始化用的内存(initmem)段
- * 并开始用户空间的程序..
- */
- init_post();
- return 0;
- }
在内核init线程的最后执行了init_post函数,在这个函数中真正启动了用户空间进程init,详解如下:
- /* 这是一个非__init函数。强制让它为非内联函数,以防 gcc
- * 让它内联到init()中并成为init.text段的一部分。
- */
- 从此函数名可知,这个函数是运行在用户空间的init程序之前
- static noinline int init_post(void)
- {
- /* 在释放内存前,必须完成所有的异步 __init 代码 */
- async_synchronize_full();
- free_initmem();
- 释放所有init.* 段中的内存。
- mark_rodata_ro();
- 通过修改页表,保证只读数据段为只读属性。大部分构架为空函数。
- system_state = SYSTEM_RUNNING;
- 设置系统状态为运行状态
- numa_default_policy();
- 设定NUMA系统的内存访问策略为默认
- current->signal->flags |= SIGNAL_UNKILLABLE;
- 设置当前进程(init)为不可以杀进程(忽略致命的信号)
- if (ramdisk_execute_command) {
- run_init_process(ramdisk_execute_command);
- printk(KERN_WARNING "Failed to execute %s\n",
- ramdisk_execute_command);
- }
- 如果ramdisk_execute_command有指定的init程序,就执行它。
- /*
- * 我们尝试以下的每个函数,直到函数成功执行.
- *
- * 如果我们试图修复一个真正有问题的设备,
- * Bourne shell 可以替代init进程。
- */
- if (execute_command) {
- run_init_process(execute_command);
- printk(KERN_WARNING "Failed to execute %s. Attempting "
- "defaults...\n", execute_command);
- }
- 如果execute_command有指定的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. "
- "See Linux Documentation/init.txt for guidance.");
- 在检查完ramdisk_execute_command和execute_command为空的情况下,顺序执行以下初始化程序:如果都没有找到就打印错误信息。这也是我们做系统移植的时候经常碰到的错误信息,出现这个信息很有可能是:
- 1、你的启动参数配置有问题,通过 指定了init程序,但是没有找到,且默认的那四个程序也不在文件系统中。
- 2、文件系统挂载有问题,文件不存在
- 3、init程序没有执行权限
- 至此,内核的初始化结束,正式进入了用户空间的初始化过程!!
- }
Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】的更多相关文章
- 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 百篇博客分析OpenHarmonyOS | v2.07
百篇博客系列篇.本篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核 ...
- Linux内核源码分析 day01——内存寻址
前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...
- Linux内核源码分析之setup_arch (四)
前言 Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由于距离上一篇时间比较久了,所以这里重新贴一下大致的流程图,本文主要分析的是bootmem_ ...
- 鸿蒙内核源码分析(根文件系统) | 先挂到`/`上的文件系统 | 百篇博客分析OpenHarmony源码 | v66.01
百篇博客系列篇.本篇为: v66.xx 鸿蒙内核源码分析(根文件系统) | 先挂到/上的文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 百篇博客分析OpenHarmony源码 | v62.01
百篇博客系列篇.本篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 51.c.h.o 本篇开始说文件系统,它是内核五大模块之一,甚至有Linux的设计哲学是" ...
- 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01
百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...
- 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 百篇博客分析OpenHarmony源码 | v51.04
百篇博客系列篇.本篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | ...
- 鸿蒙内核源码分析(信号生产篇) | 信号安装和发送过程是怎样的? | 百篇博客分析OpenHarmony源码 | v48.03
百篇博客系列篇.本篇为: v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...
- 鸿蒙内核源码分析(特殊进程篇) | 龙生龙,凤生凤,老鼠生儿会打洞 | 百篇博客分析OpenHarmony源码 | v46.02
百篇博客系列篇.本篇为: v46.xx 鸿蒙内核源码分析(特殊进程篇) | 龙生龙凤生凤老鼠生儿会打洞 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁 ...
- 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02
百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...
随机推荐
- 自学Zabbix14.1 二次开发API
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix14.1 二次开发API Zabbix API我们可以做很多,自己开发web界面. ...
- 【BZOJ2875】【NOI2012】随机数生成器(矩阵快速幂)
[BZOJ2875]随机数生成器(矩阵快速幂) 题面 Description 栋栋最近迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Me ...
- 添加AD RMS role时,提示密码不能被验证The password could not be validated
"The password could not be validated" when attempting to provision an AD RMS server. Sympt ...
- 用powershell 批量卸载 windows 更新
$KBID = "KB958488" $KBID1 = "KB976902" cls function Remove-Update { $HotFixes = ...
- .net连接ORACLE数据库
这段时间维护客户的一个系统,该系统使用的是ORACLE数据库,之前开发的时候用的都是MSSQL,并没有使用过ORACLE.这两种数据库虽然都是关系型数据库,但是具体的操作大有不同,这里作下记录. 连接 ...
- ASP.NET Session的实现原理分析
ASP.NET Session的实现原理分析 用户向服务器提交请求时,服务器都会给每个用户分配一个SessionId,保存在用户浏览器的Cookies中,SessionId是全局的,也就是说只要Coo ...
- typescript接口(学习笔记非干货)
typescript的核心原则之一就是对所具有的shape类型检查结构性子类型化 One of the core principles of typescript is to check struct ...
- 跟我一起使用electron搭建一个文件浏览器应用吧(二)
这个文件浏览器应用可以具备以下两种功能噢- This file browser application can have the following two functions. 一:用户浏览文件夹和 ...
- 有趣的async
在项目的开发过程中,同步异步是一个很重要的概念.但是在js中,又会有稍微的不同. 依据微软的MSDN上的解说: (1) 同步函数:当一个函数是同步执行时,那么当该函数被调用时不会立即返回,直到该函数所 ...
- 利用千人基因组数据库查看SNP在不同地区、国家、洲的频率及个数
首先,进入千人基因组数据库的网站:https://www.ncbi.nlm.nih.gov/variation/tools/1000genomes/ 如下图所示,在数据库的框框里输入我们感兴趣的SNP ...