内核启动的C语言阶段——start_kernel函数
以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。
start_kernel函数位于kernel/init/main.c,完成以下内容:
(1)打印一些输出信息。
(2)内核工作所需的模块的初始化被依次调用(譬如内存管理、调度系统、异常处理……)。
- 重点了解setup_arch做的2件事情:机器码架构的查找并且执行架构相关的硬件的初始化,uboot给内核的传参cmdline。
- 重点了解内核启动后的稳定状态。
一、内核的解压缩、版本信息打印
1、零碎内容
(2)lockdep。锁定依赖,是一个内核调试模块,处理内核自旋锁死锁问题相关的;
(3)cgroup。control group,内核提供的一种来处理进程组的技术。
2、解压缩
实际是在head.S中调用了下面的函数
3、打印内核版本信息
(1)printk函数是内核中用来向console打印信息的,类似于应用层编程中的printf。
- 内核编程时不能使用标准库函数,因此不能使用printf,其实printk就是内核自己实现的一个printf。
(2)printk函数的用法和printf几乎一样,不同之处在于可以在参数最前面用一个宏来定义消息输出的级别。
- 因为linux代码量太多,里面的printk打印信息太多。如果所有的printk都能打印出来而不加任何限制,则最终内核启动后得到海量的输出信息。
- linux内核给每一个printk添加一个打印级别。级别定义0-7(注意编程的时候要用相应的宏定义,不要直接用数字)分别代表8种输出的重要性级别,0表示最重要,7表示最不重要。根据自己的消息的重要性去设置打印级别。
- linux的控制台有一个消息过滤显示机制,控制台实际只会显示级别比我的控制台定义的级别高的消息。譬如说控制台的消息显示级别设置为4,那么只有printk中消息级别为0-3(也可能是0-4)的才可以显示看见。
(3)linux_banner的内容解析
- UTS_RELEASE,可以在编译后的kernel目录下,使用grep "UTS_RELEASE" * -nR指令搜索。同理可以查找其他字符的出处。
二、内核对架构信息的处理
1、setup_arch函数简介
(1)此函数是CPU架构相关的一些创建过程。
(2)此函数用来确定当前内核对应的硬件平台(arch、machine)。
- linux内核会支持一种CPU的运行,而CPU+开发板就确定了一个硬件平台。经过配置的内核可以在此平台上运行。
- 机器码是硬件平台的一个固定的编码,用来表征这个平台。
(3)当前内核支持的机器码、硬件平台相关的一些定义,都在此函数中处理。
2、setup_arch函数内部的setup_processor函数
(1)此函数用来查找CPU信息,可以结合串口打印的信息来分析。
3、setup_arch函数内部的setup_machine函数
(1)传参是机器码编号
- machine_arch_type符号在include/generated/mach-types.h的32039-32050行定义,经过分析后确定这个传参值就是2456。
(2)函数的作用
- 通过传入的机器码编号,找到对应这个机器码的machine_desc描述符,并且返回这个描述符的指针。
(3)真正干活的函数是lookup_machine_type
- 此函数在head-common.S中,分析得知真正干活的函数是__lookup_machine_type。
(4)__lookup_machine_type函数的工作原理
- 内核在建立时,把各种CPU架构的信息组织成若干machine_desc结构体实例,然后设置段属性.arch.info.init,链接的时候,这些描述符就会被连接在一起。
- __lookup_machine_type函数到描述符所在处,依次遍历各个描述符,比对看机器码哪个相同。
链接脚本截图:
_lookup_machine_type函数截图:
三、内核对bootargs的处理
1、setup_arch函数进行基本的cmdline处理
(1)cmdline,指uboot给kernel传参时传递的命令行启动参数,即uboot的bootargs。
(2)有几个相关的变量需要注意
- default_command_line是默认的命令行参数,实际是一个全局变量字符数组。
- CONFIG_CMDLINE,在.config文件中定义(可以在make menuconfig中去更改设置),它表示内核的默认的命令行参数。
(3)内核对cmdline的处理思路
- 内核中维护一个默认的cmdline(即.config中所配置的那个),uboot也可以通过tag给kernel再传递一个cmdline。
- 如果uboot给内核传cmdline成功,则内核会优先使用uboot所传递的;如果uboot没有给内核传cmdline或者传参失败,则内核会使用默认的cmdline。
- 内核为什么要这样设计?因为希望内核是灵活的,可以通过传参对内核进行配置。
- 这个处理思路在setup_arch函数中实现。
2、setup_command_line函数
处理和命令行参数cmdline有关的任务。
3、parse_early_param函数、parse_args函数
(1)解析cmdline传参和其他传参
- 即,将cmdline的细节设置信息给解析出来。
- 譬如cmdline:console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,则解析出的内容就是就是一个字符串数组。
- 数组中依次存放了一个设置项目信息。
(2)只是进行解析,并没有去处理
- 只是把长字符串解析成短字符串,最多和内核里控制这个相应功能的变量挂钩了,但是并没有去执行。
- 执行的代码在各自模块初始化的代码部分。
四、start_kernel杂碎的函数
(1)start_kernel函数中调用了很多的xx_init函数,全都是内核工作需要的模块的初始化函数。
- trap_init设置异常向量表
- mm_init内存管理模块初始化
- sched_init内核调度系统初始化
- early_irq_init&init_IRQ中断初始化
- console_init控制台初始化
(2)这些初始化之后,内核就具备了基本的工作条件。
- 如果把内核比喻成一个复杂机器,那么start_kernel函数就是把这个机器的众多零部件组装在一起形成这个机器,让其具有可以工作的基本条件。
五、start_kernel末尾的rest_init函数
1、rest_init函数
此函数之前内核的基本组装已经完成,剩下的一些工作放在一个单独的函数中,叫rest_init。
(1)rest_init中调用kernel_thread函数,启动了2个内核线程,分别是:kernel_init和kthreadd。
(2)调用schedule函数开启内核的调度系统,从此linux系统开始转起来。
(3)rest_init最终调用cpu_idle函数结束整个内核的启动,即linux内核最终结束于cpu_idle函数。
(4)linux内核最终的状态
- linux内核最终结束于cpu_idle函数,此函数里面是死循环。
- 调度系统负责考评系统中所有的进程。只要有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。
2、什么是内核线程?
- 一个运行的程序就是一个进程,这个程序和别的程序是分开的,这个程序可以被内核单独调用执行或者暂停。
- 应用层运行一个程序就构成一个用户进程/线程;
- 内核中运行一个函数(函数其实就是一个程序)就构成一个内核进程/线程。
- 因此kernel_thead函数运行一个函数,就是把这个函数变成一个内核线程,可以被内核调度系统去调度,即去调度器注册,将来调度的时候会进行考虑。
3、进程0、进程1、进程2
(1)截至目前为止,我们一共涉及到3个内核进程/线程。操作系统用数字来表示/记录一个进程/线程,此数字就被称为这个进程的进程号。这个号码是从0开始分配的。
- 进程0:idle进程,叫空闲进程,也就是死循环。
- 进程1:kernel_init函数就是进程1,这个进程被称为init进程。
- 进程2:kthreadd函数就是进程2,这个进程是linux内核的守护进程。这个进程是用来保证linux内核自己本身能正常工作的。
(2)在linux命令行下,使用ps命令可以查看当前linux系统中运行的进程情况。
(3)在ubuntu下ps -aux可以看到当前系统运行的所有进程,可以看出进程号是从1开始的。
- 为什么不从0开始,因为进程0不是一个用户进程(查询不到),而属于内核进程。
学习方法
(1)学习思路
- 抓大放小,不深究;
- 感兴趣可以就某个话题去网上搜索资料学习;
- 重点局部深入分析;
(2)具体学习方法
- 顺着代码执行路径抓全,这是我们的学习主线;
- 对照内核启动的打印信息进行分析。
(3)几条学习线路
- 分析uboot给kernel传参的影响和实现;
- 硬件初始化与驱动加载;
- 内核启动后的结局与归宿。
内核启动的C语言阶段——start_kernel函数相关推荐
- Linux内核4.14版本:ARM64的内核启动过程(二)——start_kernel
目录 1. rest_init 2. init 进程(kernel_init) 2.1 kernel_init_freeable 2.1.1 do_basic_setup 2.1.2 prepare_ ...
- Linux 内核启动流程
转载自 http://wenku.baidu.com/link?url=KpOdULJu1CxP1swqRs_Szoyg5r_8rje4N08o4QtB5L9QlPjWesTYlrTPgkxPOriF ...
- Linux内核启动过程概述
Hi!大家好,我是CrazyCatJack.今天给大家带来的是Linux内核启动过程概述.希望能够帮助大家更好的理解Linux内核的启动,并且创造出自己的内核^_^ Linux的启动代码真的挺大,从汇 ...
- linux内核启动以及文件系统的加载过程
Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...
- linux内核启动第一个进程,linux内核启动流程
描述 Linux的启动代码真的挺大,从汇编到C,从Makefile到LDS文件,需要理解的东西很多.毕竟Linux内核是由很多人,花费了巨大的时间和精力写出来的.而且直到现在,这个世界上仍然有成千上万 ...
- 内核启动的汇编阶段——head.S文件
以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 汇编阶段主要是arch/arm/kernel/目录下的head.S文件,主要完成以下内容: (1)校验启动合法性:(CPU ID,机器码,u ...
- kernel 启动过程之三, start_kernel()函数 概叙!init/main.c
http://blog.csdn.net/pottichu/article/details/4261228 核心数据结构初始化--内核引导第一部分 start_kernel()中调用了一系列初始化函数 ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】...
原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinauni ...
- 内核启动分析(三)——zImage 解压缩阶段
在上阶段,主要是U-BOOT 向内核传递一些参数.而这些参数是通过 struct tag来传递的.U-boot 把要传递给 kernel 的东西保存在 struct tag 数据结构中,启 ...
最新文章
- Apollo 自动驾驶开发套件(D-KIT)
- mysql中的字符匹配查询
- TJU 2248. Channel Design 最小树形图
- 2011年9月最新整理的10个有趣的jQuery插件集合
- wpf开发仿真3d软件_web 3d 与仿真
- vue 判断同一数组内的值是否一直_前端代码+后端API,值得一学的Vue高仿音乐播放器实战项目
- 【转载】那么明亮的sz4j
- jmxtrans安装使用
- phpstudy thinkphp5 mysql5.5+存储emoji
- pythonic code_Pythonic Code (Part III)
- Chrome 100发布:启用全新图标,修复28个安全漏洞
- error: Program received signal SIGSEGV, Segmentation fault. (Codeblocks, C++)(2)
- 计算机体系结构五大部分组成
- C语言中的一些基本函数说明及使用
- 开启微信公众号定位服务器,微信公众号定位学会这几步就够了!
- 【Cherno的OpenGL视频】Uniforms in OpenGL
- Python之闭包与装饰器
- 安装virtualbox快完成时立即回滚,并提示安装出现严重错误
- OKR实施方法——关于思路和流程的思考
- 一台显示器,两台不同的局域网计算机的切换需求
热门文章
- MessageBox 弹框
- hadoop fs 运维常用的几个命令
- 网络流(最大流) HDU 1565 方格取数(1) HDU 1569 方格取数(2)
- 极大似然估计 摘自维基百科
- Google的投票站点在用Asp.net
- Python--day48--ORM框架SQLAlchemy操作表
- AS 2.0新功能 Instant Run
- Memcached 内存管理(一)
- poj2299 ( bit )
- JavaScript 正则表达式(RegExp对象、属性、方法、String支持)