16_Linux内核启动流程

news2024/12/25 9:25:07

目录

链接脚本vmlinux.Ids

Linux内核入口stext

mmap_switched函数

start_kernel函数

rest_init函数

init进程


链接脚本vmlinux.Ids

要分析Linux启动流程,同样需要先编译一下Linux源码,因为有很多文件是需要编译才会生成的。首先分析Linux内核的连接脚本文件arch/arm/kemel/vmlinux.lds,通过链接脚本可以找到Linux内核的第一行程序是从哪里执行的。vmlinux.lds 中有如下代码:

 第493行的ENTRY指明了了Linux内核入口,入口为stext, stext定义在文件arch/arm/kernel/head.S 中,因此要分析Linux内核的启动流程,就得先从文件arch/arm/kernel/head.S的stext处开始分析。

Linux内核入口stext

stext是Linux内核的入口地址,在文件arch/arm/kernel/head.S中有如下所示提示内容:

根据示例代码36.2.1.1中的注释, Linux 内核启动之前要求如下:

1.关闭 MMU。

2.关闭D-cache。

3.I-Cache 无所谓。

4.r0=0。

5.rl=machine nr(也就是机器ID)。

6.r2-atags或者设备树(dtb)首地址。

Linux内核的入口点stext其实相当于内核的入口函数, stext函数内容如下:

第92行,调用函数safe_svcmode_maskall确保CPU处于SVC模式,并且关闭了所有的中断。safe_svcmode_maskall定义在文件arch/arm/include/asm/assembler.h中。

第94行,读处理器 ID,ID值保存在r9寄存器中。

第95行,调用函数_lookup_processor_type检查当前系统是否支持此CPU,如果支持就获取procinfo信息。procinfo是proc info list类型的结构体, proc info list在文件

arch/arm/include/asm/procinfo.h中的定义如下:

 Linux内核将每种处理器都抽象为一个proc info_list结构体,每种处理器都对应一个procinfo。因此可以通过处理器ID来找到对应的procinfo结构, lookup_processor_type函数找到对应处理器的procinfo以后会将其保存到r5寄存器中。

继续回到示例代码36.2.1.2中,第121行,调用函数_vet_atags 验证 atags 或设备树(dtb)的合法性。函数vet_atags定义在文件arch/arm/kernel/head-common.S中。

第128行,调用函数create_page_tables创建页表。

第137行,将函数mmap_switched的地址保存到r13寄存器中。mmap_switched定义在文件 arch/arm/kernel/head-common.S, _mmap_switched最终会调用start_kernel 函数。

第144行,调用enable_mmu函数使能MMU, enablemmu定义在文件

arch/arm/kerne/head.S中。_enable_mmu最终会通过调用_turn_mmu_on来打开MMU,_turn_mmu_on最后会执行r13里面保存的_mmap_switched函数。

mmap_switched函数

mmap switched函数定义在文件arch/arm/kernel/head-common.S中,函数代码如下:

 第104行最终调用start_kernel来启动Linux内核, start_kernel函数定义在文件init/main.c中。

 

start_kernel函数

start_kernel通过调用众多的子函数来完成Linux启动之前的一些初始化工作,由于start_kernel函数里面调用的子函数太多,而这些子函数又很复杂,因此我们简单的来看一下一些重要的子函数。精简并添加注释后的start_kemel函数内容如下:

 start_kernel里面调用了大量的函数,每一个函数都是一个庞大的知识点,如果想要学习Linux 内核,那么这些函数就需要去详细的研究。start_kernel函数最后调用了rest_init,接下来简单看一下rest_init函数。

rest_init函数

rest_init函数定义在文件init/main.c,函数内容如下:

 第387行,调用函数rcu_scheduler_starting,启动RCU锁调度器

第394行,调用函数kernel_thread创建kemel_init进程,也就是大名鼎鼎的init内核进程。init 进程的PID为 1。init进程一开始是内核进程(也就是运行在内核态),后面init进程会在根文件系统中查找名为"init”这个程序,这个"init”程序处于用户态,通过运行这个“init”程序,init进程就会实现从内核态到用户态的转变。

第396行,调用函数kernel_thread创建kthreadd内核进程,此内核进程的PID为2。kthreadd进程负责所有内核进程的调度和管理。

第409行,最后调用函数cpu_startup_entry来进入idle进程, cpu_startup_entry会调用进程负责所有内核进程的调度和管理。

第409行,最后调用函数cpu_startup_entry来进入idle进程, cpu_startup_entry会调用cpu_idle_loop, cpu_idle_loop是个while循环,也就是idle进程代码。idle进程的PID为0, idle进程叫做空闲进程,如果学过FreeRTOS或者UCOS的话应该听说过空闲任务。idle空闲进程就和空闲任务一样,当CPU没有事情做的时候就在idle空闲进程里面“瞎逛游”,反正就是给CPU找点事做。当其他进程要工作的时候就会抢占idle进程,从而夺取CPU使用权。其实大家应该可以看到idle进程并没有使用kernel_thread或者fork函数来创建.因为它是有主进程演变而来的。

在Linux终端中输入“ps -A”就可以打印出当前系统中的所有进程,其中就能看到init进程和kthreadd进程,如图所示:

从图可以看出,init进程的PID为1, kthreadd进程的PID为2。之所以图中没有显示PID为0的idle进程,那是因为idle进程是内核进程。接下来重点看一下init进程, kernel_init就是init进程的进程函数。 

init进程

Kernel_init函数就是init进程具体做的工作,定义在文件init/main.c中,函数内容如下:

 第932行, kernel_init_freeable函数用于完成init进程的一些其他初始化工作,稍后再来具体看一下此函数。

第940行,ramdisk_ekecute_command是一个全局的char指针变量,此变量值为"/init",也就是根目录下的init程序。ramdisk_execute_command也可以通过uboot传递,在bootargs中使用“rdinit-xxx”即可,xxx为具体的init程序名字。

第943行,如果存在“/init”程序的话就通过函数run_init process来运行此程序。

第956行,如果ramdisk_execute_command为空的话就看execute_command是否为空,反正不管如何一定要在根文件系统中找到一个可运行的init程序。execute_command的值是通过.uboot传递,在bootargs中使用“init=xxxx”就可以了,比如“init=/inuxrc”表示根文件系统中的linuxrc就是要执行的用户空间init程序。

第963-966行,如果ramdisk_execute_command和execute_command都为空,那么就依次查找"/sbin/init"、"/etc/init"、"/bin/init"和"/bin/sh",这四个相当于备用init程序,如果这四个也不存在,那么Linux启动失败!

第969行,如果以上步骤都没有找到用户空间的init程序,那么就提示错误发生!

最后来简单看一下kernel_init_freeable函数,前面说了, kernel_init会调用此函数来做一些init进程初始化工作。kernel init freeable定义在文件init/main.c中,缩减后的函数内容如下:

 第1002行, do_basic_setup函数用于完成Linux下设备驱动初始化工作!非常重要。do_basic_setup会调用driver_init函数完成Linux下驱动模型子系统的初始化。

第1005行,打开设备"/dev/console",在Linux中一切皆为文件!因此"/dev/console"也是一个文件,此文件为控制台设备。每个文件都有一个文件描述符,此处打开的"/dev/console"文件描述符为 0,作为标准输入(0)

第1008和1009行, sys-dup函数将标准输入(0)的文件描述符复制了2次,一个作为标准输出(1),一个作为标准错误(2)。这样标准输入、输出、错误都是/dev/console了。console通过uboot的bootargs环境变量设置,"console=ttymxc0,115200"表示将/dev/ttymxc0设置为console,也就是I.MX6U的串口1。当然,也可以设置其他的设备为console,比如虚拟控制台tty1,设置tty1为console就可以在LCD屏幕上看到系统的提示信息。

第1020行,调用函数prepare_namespace来挂载根文件系统。根文件系统也是由命令行参·数指定的,就是uboot的bootargs环境变量。比如"root=/dev/mmeblk1p2 rootwait rw”就表示根文件系统在/dev/mmcblklp2中,也就是EMMC的分区2中。

Linux内核启动流程就分析到这里, Linux内核最终是需要和根文件系统打交道的,需要挂载根文件系统,并且执行根文件系统中的init程序,以此来进去用户态。这里就正式引出了根·文件系统,根文件系统也是我们系统移植的最后一片拼图。Linux移植三巨头: uboot、 Linuxkernel、rootfs(根文件系统)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/604028.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】Python系列教程-- Python3 字符串(十一)

文章目录 前言Python3 字符串Python 访问字符串中的值Python 字符串更新Python 转义字符Python 字符串运算符Python 字符串格式化Python三引号f-stringUnicode 字符串Python 的字符串内建函数 前言 往期回顾: Python系列教程–Python3介绍(一&#xff…

计算机网络-网络层1.0

传输单位 网络层将分组从源端传到目的端,为分组交换网上的不同主机提供通信服务,传输单位为数据报 路由器 完成路由选择和分组转发 路由选择:按照分布式算法,根据从相邻路由器所得到的的关于整个网络拓扑的变化情况&#xff0…

操作系统第二章​练习题

第一部分 教材习题(P84) 1、什么是前趋图?为什么要引入前趋图?​ 前趋图:是用于描述程序段或进程之间执行的先后顺序的有向无循环图。 引入的原因:为了更好的描述程序的顺序和并发执行的情况。 试画出下面4条语句的前趋图&#…

LeetCode 2559 统计范围内的元音字符串数

LeetCode 2559 统计范围内的元音字符串数 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/count-vowel-strings-in-ranges/description/ 博主Github:https://github.com/GDUT-Rp/LeetCode 题目:…

Vue3小兔鲜:组合式写法入门

Vue3&#xff1a;组合式写法入门 Date: May 11, 2023 认识Vue3 1. Vue3组合式API体验 通过 Counter 案例 体验Vue3新引入的组合式API <script> export default {data(){return {count:0}},methods:{addCount(){this.count}} } </script><script setup> imp…

【JavaSE】Java基础语法(四十四):XML解析

文章目录 1. 概述2.标签的规则3. 语法规则【应用】4. xml解析【应用】 1. 概述 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域…

【分布族谱】高斯分布和逆高斯分布的关系

文章目录 高斯分布逆高斯分布简介通过高斯分布构造逆高斯分布 高斯分布 正态分布&#xff0c;又称Gauss分布&#xff0c;其概率密度函数入下图所示 正态分布 N ( μ , σ ) N(\mu, \sigma) N(μ,σ)受到期望 μ \mu μ和方差 σ 2 \sigma^2 σ2的调控&#xff0c;其概率密度函…

基于SpringBoot+Vue的医疗服务系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

Qt for Android环境配置(联合Android Studio)

目录 1.安装JDK2.安装Android Studio并下载必要组件3.安装QtCreator并配置 Official Doc 1.安装JDK Oracle JDK Download 配置环境变量&#xff1a; 2.安装Android Studio并下载必要组件 Android Studio Download 下图中&#xff0c;31.0.0是当前Qt必需的&#xff0c;3…

chatgpt赋能python:Python制作抽奖——让你的活动更有趣

Python制作抽奖——让你的活动更有趣 在活动中&#xff0c;抽奖环节常常是一大亮点。而用Python制作抽奖程序&#xff0c;则可以更灵活、更实用地完成这个环节。接下来我们将介绍Python制作抽奖的方法&#xff0c;并且说明如何让你的抽奖环节更加有趣。 Python制作抽奖的方法…

【Linux网络服务】Nginx优化

Nginx网页优化 一、配置Nginx网页缓存时间1.1设置方法 二、隐藏Nginx版本号2.1方法一&#xff1a;修改配置文件2.2方法二&#xff1a;修改源码文件&#xff0c;重新编译 三、修改用户与组四、日志切割五、连接超时六、更改进程数七、网页压缩八、配置防盗链九、Linux内核参数优…

华为OD机试真题 Java 实现【素数之积】【2022Q4 100分】

一、题目描述 RSA加密算法在网络安全世界中无处不在&#xff0c;它利用了极大整数因数分解的困难度&#xff0c;数据越大&#xff0c;安全系数越高。 给定一个32位正整数&#xff0c;请对其进行因数分解&#xff0c;找出是哪两个素数的乘积。 二、输入描述 一个正整数num …

基于物理信息的神经网络(Physics-informed Neural Networks;PINNs)Part-1(简单介绍)

【摘要】 基于物理信息的神经网络&#xff08;Physics-informed Neural Networks&#xff0c;简称PINNs&#xff09;&#xff0c;是一类用于解决有监督学习任务的神经网络&#xff0c;它不仅能够像传统神经网络一样学习到训练数据样本的分布规律&#xff0c;而且能够学习到数学…

段的概念-汇编复习(3)

本文小节讨论内容&#xff1a;"段地址x16偏移地址物理地址”的本质含义,段 的 概 念 "段地址x16偏移地址物理地址”的本质含义 注意&#xff0c;这里讨论的是 8086CPU 段地址和偏移地址的本质含义&#xff0c;而不是为了解决具体的问题而在本质含义之上引申出来的更…

Node.js基本概念、特点、用途和常用模块,以及Express框架开发一个web应用

目录 一、Node.js的基本概念和特点 二、Node.js的用途 三、Node.js的常用模块 四、使用Node.js进行Web开发 1. 安装Node.js 2. PyCharm配置Node.js 3. 使用http库编写一个web服务 4. 使用Express框架构建Web应用程序 5. 调试代码 6. 发布应用程序 参考文章 Node.js是…

[转载]Nginx 使用 X-Accel-Redirect 实现静态文件下载的统计、鉴权、防盗链、限速等

需求 统计静态文件的下载次数&#xff1b;判断用户是否有下载权限&#xff1b;根据用户指定下载速度&#xff1b;根据Referer判断是否需要防盗链&#xff1b;根据用户属性限制下载速度&#xff1b; X-Accel-Redirect This allows you to handle authentication, logging or …

基于vue3全新后台管理方案vite4+vue3+pinia2+vue-i18n

Vite4-Admin 基于 vue3vite4.xpinia2vue-router4 构建后台管理系统。 支持vue-i18n国际化多语言、动态路由鉴权、4种布局模板及tab页面缓存等功能。 技术框架 编码器&#xff1a;VScode框架技术&#xff1a;vite4vue3piniavue-routerUI组件库&#xff1a;ve-plus (基于vue3自研…

PHP快速实战19-PHP使用IMAP获取QQ邮箱的收件列表及内容

文章目录 前言关于IMAP协议安裝与实现步骤1&#xff1a;安装IAMP扩展1.1 检查IMAP是否已安装1.2 安装IMAP扩展1.3 启用IMAP扩展1.4 重启服务 步骤2&#xff1a;开始功能实现2.1 开始编码2.2 代码执行 常见的20个PHP中IMAP方法总结 前言 本文已收录于PHP全栈系列专栏&#xff1…

详解js中的浅拷贝与深拷贝

详解js中的浅拷贝与深拷贝 1、前言1.1 栈&#xff08;stack&#xff09;和堆&#xff08;heap&#xff09;1.2 基本数据类型和引用数据类型1.2.1 概念1.2.2 区别1.2.3 基本类型赋值方式1.2.4 引用类型赋值方式 2、浅拷贝2.1 概念2.2 常见的浅拷贝方法2.2.1 Object.assign()2.2.…

25 strlen 的调试

前言 同样是一个 很常用的 glibc 库函数 不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 字符串长度的计算 不过 我们这里是从 具体的实现 来看一下 它的实现 主要是使用 汇编 来进行实现的, 因此 理解需要一定的基础 测试用例 就是简单的使用了一下 strc…