Linux操作系统学习——启动

news2024/12/23 17:58:46


 概要

  Linux操作系统内核是服务端学习的根基,也是提高编程能力、源码阅读能力和进阶知识学习能力的重要部分,本文开始将记录Linux操作系统中的各个部分源码学习历程。

1. 理解代码的组织结构

Linux源码举例,首先你得知道操作系统分为哪几个部分,他们单独做了什么功能,如何进行配合完成更为具体的功能。建立整体的印象有助于后续深入学习的时候方便理解,毕竟代码是用的不是看的,理解他的作用有利于理解为什么要这么做。

2. 深入各个模块学习

模块接口:这里推荐微软的画图工具visio或者思维导图xmind,用其画图可以将各个模块的接口列出,并绘制各个模块之间的关系,通过了解接口可以清楚各个模块之间的关系,即绘制模块组织图

工作流程:通过上面一步得到各模块间的关系,然后实际用断点或log等方式看一看整体的工作流程,在模块组织图的基础上绘制程序流程图

模块粘合层:我们的代码有很多都是用来粘合代码的,比如中间件(middleware)、Promises 模式、回调(Callback)、代理委托、依赖注入等。这些代码模块间的粘合技术是非常重要的,因为它们会把本来平铺直述的代码给分裂开来,让你不容易看明白它们的关系。这些可以作为程序流程图的补充,让其中本来无法顺畅衔接的地方变得通畅无阻。

模块具体实现 :这是最难得地方,涉及到大量具体源码的学习。深入细节容易迷失在细节的海洋里,因此需要有一些重点去关注,将非重点的内容省略。通过学习绘制模块具体架构图和模块的算法时序图,可以帮助你更好的掌握源码的精髓。

3. 需要关注的包括

代码逻辑。代码有两种逻辑,一种是业务逻辑,这种逻辑是真正的业务处理逻辑;另一种是控制逻辑,这种逻辑只是用控制程序流转的,不是业务逻辑。比如:flag 之类的控制变量,多线程处理的代码,异步控制的代码,远程通讯的代码,对象序列化反序列化的代码等。这两种逻辑你要分开,很多代码之所以混乱就是把这两种逻辑混在一起了。

重要的算法。一般来说,我们的代码里会有很多重要的算法,我说的并不一定是什么排序或是搜索算法,可能会是一些其它的核心算法,比如一些索引表的算法,全局唯一 ID 的算法、信息推荐的算法、统计算法、通读算法(如 Gossip)等。这些比较核心的算法可能会非常难读,但它们往往是最有技术含量的部分。

底层交互。有一些代码是和底层系统的交互,一般来说是和操作系统或是 JVM 的交互。因此,读这些代码通常需要一定的底层技术知识,不然,很难读懂。

4. 可以忽略的包括

出错处理。根据二八原则,20% 的代码是正常的逻辑,80% 的代码是在处理各种错误,所以,你在读代码的时候,完全可以把处理错误的代码全部删除掉,这样就会留下比较干净和简单的正常逻辑的代码。排除干扰因素,可以更高效地读代码。

数据处理。只要你认真观察,就会发现,我们好多代码就是在那里倒腾数据。比如 DAODTO,比如 JSONXML,这些代码冗长无聊,不是主要逻辑,可以不理。

忽略过多的实现细节。在第一遍阅读源码时,已弄懂整体流程为主,至于具体的实现细节先简单的理清处过一遍,不用过于纠结。当梳理清楚全部的框架逻辑后,第二遍再深入的学习研究各个模块的实现,此时应该解决第一遍中的疑惑。第三遍可以跳出代码的实现,来看Linux的设计思路、编程艺术和演进之路。

重在实践Linux的代码都是可以调试的,看很多遍也许不如跟着调试走一遍,然后再自己修改修改做一些小测试。

传授知识。当你能将知识讲述给别人听,并让别人听懂时,你已经可以自豪的说洞悉了这些知识。所以不妨从一个小的例子开始自说自话,看能不能自圆其说,甚至写成博客、做成PPT给大家讲解。

  说了一大堆的废话,下面就正式开始操作系统的深入学习记录之旅了。

5. 混沌初开

  本文分析从按下电源键到加载BIOS以及后续bootloader的整个过程。犹如盘古开天辟地一般,该过程将混沌的操作系统世界分为清晰的内核态和用户态,并经历从实模式到保护模式的变化。这里先简单介绍一下名词,便于后续理解。

实模式Real Mode):又名 Real Address Mode,在此模式下地址访问的是真实地内存地址所在位置。在此模式下,可以使用20位(1MB)的地址空间,软件可以不受限制的操作所有地址的空间和IO设备。

保护模式Protected Mode):又名 Protected Virtual Address Mode,采用虚拟内存、页等机制对内存进行了保护,比起实模式更为安全可靠,同时也增加了灵活性和扩展性。

 5.1 从启动电源到BIOS 

当我们按下电源键,主板会发向电源组发出信号,接收到信号后,电源会提供合适的电压给计算机。当主板收到电源正常启动的信号后,主板会启动CPUCPU重置所有寄存器数据,并设置初始化数据,这个初始化数据在X86架构里如下所示:

1IP          0xfff0

2CS selector 0xf000

3CS base     0xffff0000

4IP/EIP (Instruction Pointer) : 指令指针寄存器,记录将要执行的指令在代码段内的偏移地址

5CS(Code Segment Register):代码段寄存器,指向CPU当前执行代码在内存中的区域(定义了存放代码的存储器的起始地址)

实模式采取内存段来管理 0 - 0xFFFFF的这1M内存空间,但是由于只有16位寄存器,所以最大地址只能表示为0xFFFFF64KB),因此不得不采取将内存按段划分为64KB的方式来充分利用1M空间。也就是上所示的,采取段选择子 + 偏移量的表示法。这种方法在保护模式中对于页的设计上也沿用了下来,可谓祖传的智慧了。具体的计算公式如下所示:

1PhysicalAddress = Segment Selector * 16 + Offset

  该部分由硬件完成,通过计算访问0XFFFF0,如果该位置没有可执行代码则计算机无法启动。如果有,则执行该部分代码,这里也就是我们故事的开始,BIOS程序了。

 5.2 BIOS到BootLoader 

  BIOS执行程序存储在ROM中,起始位置为0XFFFF0,当CS:IP指向该位置时,BIOS开始执行。BIOS主要包括以下内存映射:

 10x00000000 - 0x000003FF - Real Mode Interrupt Vector Table

 20x00000400 - 0x000004FF - BIOS Data Area

 30x00000500 - 0x00007BFF - Unused

 40x00007C00 - 0x00007DFF - Our Bootloader

 50x00007E00 - 0x0009FFFF - Unused

 60x000A0000 - 0x000BFFFF - Video RAM (VRAM) Memory

 70x000B0000 - 0x000B7777 - Monochrome Video Memory

 80x000B8000 - 0x000BFFFF - Color Video Memory

 90x000C0000 - 0x000C7FFF - Video ROM BIOS

100x000C8000 - 0x000EFFFF - BIOS Shadow Area

110x000F0000 - 0x000FFFFF - System BIOS

12

其中最重要的莫过于中断向量表和中断服务程序。BIOS程序在内存最开始的位置(0x00000)用1 KB的内存空间(0x000000x003FF)构建中断向量表,在紧挨着它的位置用256字节的内存空间构建BIOS数据区(0x004000x004FF),并在大约57 KB以后的位置(0x0E05B)加载了8 KB左右的与中断向量表相应的若干中断服务程序。中断向量表中有256个中断向量,每个中断向量占4字节,其中两个字节是CS的值,两个字节是IP的值。每个中断向量都指向一个具体的中断服务程序。

 BIOS程序会选择一个启动设备,并将控制权转交给启动扇区中的代码。主要工作即使用中断向量和中断服务程序完成BootLoader的加载,最终将boot.img加载至0X7C00的位置启动。Linux内核通过Boot Protocol定义如何实现该引导程序,有如GRUB 2syslinux等具体实现方式,这里仅介绍GRUB2

5.3 BootLoader的工作

  boot.imgboot.S编译而成,512字节,安装在启动盘的第一个扇区,即MBR。由于空间有限,其代码十分简单,仅仅是起到一个引导的作用,指向后续的核心镜像文件,即core.imgcore.img包括很多重要的部分,如lzma_decompress.imgdiskboot.imgkernel.img等,结构如下图。

 整个流程如下:

1boot.img加载core.img的第一个扇区,即diskboot.img,对应代码为diskboot.S

2、diskboot.img加载core.img的其他部分模块,先是解压缩程序 lzma_decompress.img,再往下是 kernel.img,最后是各个模块 module 对应的映像。这里需要注意,它不是 Linux 的内核,而是 grub 的内核。注意,lzma_decompress.img 对应的代码是 startup_raw.S,本来 kernel.img 是压缩过的,现在执行的时候,需要解压缩。

3、加载完core之后,启动grub_main函数。

4、grub_main函数初始化控制台,计算模块基地址,设置 root 设备,读取 grub 配置文件,加载模块。最后,将 GRUB 置于 normal 模式,在这个模式中,grub_normal_execute (from grub-core/normal/main.c) 将被调用以完成最后的准备工作,然后显示一个菜单列出所用可用的操作系统。当某个操作系统被选择之后,grub_menu_execute_entry 开始执行,它将调用 GRUB boot 命令,来引导被选中的操作系统。

       在这之前,我们所有遇到过的程序都非常非常小,完全可以在实模式下运行,但是随着我们加载的东西越来越大,实模式这 1M 的地址空间实在放不下了,所以在真正的解压缩之前,lzma_decompress.img 做了一个重要的决定,就是调用 real_to_prot,切换到保护模式,这样就能在更大的寻址空间里面,加载更多的东西。

  开机时的16位实模式与内核启动的main函数执行需要的32位保护模式之间有很大的差距,这个差距谁来填补?head.S做的就是这项工作。就像 kernel boot protocol 所描述的,引导程序必须填充 kernel setup header (位于 kernel setup code 偏移 0x01f1 处) 的必要字段,这些均在head.S中定义。在这期间,head程序打开A20,打开pepg,废弃旧的、16位的中断响应机制,建立新的32位的IDT……这些工作都做完了,计算机已经处在32位的保护模式状态了,调用32位内核的一切条件已经准备完毕,这时顺理成章地调用main函数。后面的操作就可以用32位编译的main函数完成,从而正式启动内核,进入波澜壮阔的Linux内核操作系统之中。

 6. 总结 

  本文介绍了从按下电源开关至加载完毕BootLoader的整个过程,后续将继续分析从实模式进入保护模式,从而启动内核创建0号、1号、2号进程的整个过程。

欢迎转发点赞评论,感谢!

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

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

相关文章

电脑怎么录屏?推荐2款录制电脑屏幕的软件!

案例:我经常需要把电脑上的内容分享给别人,一般通过手机拍摄的方式。这就导致视频十分模糊,给人的观感不太好,有没有什么方法可以实现在电脑上直接录屏? 【我想录制我的电脑屏幕上的内容分享给别人,但是我…

常用JVM命令

top 展示 进程运行的完整命令行的话可以用 top -c ,当命令行较长无法分辨是哪个程序,可使用键盘右键将窗口不断滑动至右侧查看。 uptime jps 查看当前正在运行的java进程 执行结果: pid 运行文件 [roottest1 ~]# jps 24001 rs-medical-rp…

第六期:链表回文旋转的秘密

文章目录 1. 反转链表2. 回文链表代码1. 反转链表2. 回文链表 PS:每道题解题方法不唯一,欢迎讨论!每道题后都有解析帮助你分析做题,答案在最下面,关注博主每天持续更新。 1. 反转链表 题目描述: 给你单链表…

九、会话控制——cookie、session、token

文章目录 前言一、cookie1.1 cookie 是什么1.2 cookie 的特点1.3 cookie 的运行流程1.4 express 框架中设置cookie1.5 express 框架中删除cookie1.6 express 框架中获取cookie 二、session2.1 session 是什么2.2 session 的作用2.3 session 的运行流程2.4 session 和 cookie 的…

vue框架一

目录 友情提醒第一章、vue框架是前端js框架1.1)介绍vue1.2)vue设计思想1.3)vue引入 第二章、实现的步骤2.1)创建步骤2.2)基本语法2.3) 第三章、遇到的问题3.1)3.2)3.3) 第…

python grpc状态码用法

测试代码 新建demo.proto文件定义协议缓冲区,写入以下内容: syntax "proto3"; package server;service Demo {rpc ping(Request) returns (Response) {} }message Request {string msg 1;} message Response {string msg 1;}使用gRPC的工…

百兆网和千兆网怎么接线

一、认识网线 网线:橙色和白橙交织在一起,绿色和白绿交织在一起,棕色和白棕交织在一起,蓝色和白蓝交织在一起。在传输电信号时,相互之间辐射出的电磁波会相互抵消,可以消除相互之间的干扰,所以…

shell脚本进阶1——精读ansible+shell脚本

文章目录 一、脚本规划思路二、主控机shell脚本2.1 脚本输出字体特效2.2 生成菜单栏对话框2.3 配置本地yum源仓库2.4 配置受控机yum源2.5 关闭防火墙和selinux2.6 把docker安装包给受控机2.7 安装docker-compose2.8 安装docker2.9 安装ansible2.10 安装pip2.11 主控本机免密登录…

JAVA并发十二连招

1、HashMap 面试第一题必问的 HashMap,挺考验Javaer的基础功底的,别问为啥放在这,因为重要!HashMap具有如下特性: HashMap 的存取是没有顺序的。 KV 均允许为 NULL。 多线程情况下该类安全,可以考虑用 Ha…

JavaFx Shape形状

JavaFx Shape形状 1、相关子类2、屏幕坐标系3、JavaFx SVG矢量图4、Polygon箭头形状 javafx.scene.shape.Shape 1、相关子类 javafx.scene.shape.Linew3cschool JavaFX 线javafx.scene.shape.Rectanglew3cschool JavaFX 矩形椭圆javafx.scene.shape.Ellipsejavafx.scene.shap…

开源办公套件 ONLYOFFICE文档7.4新功能

随着现代办公的信息化发展,我们处理文档时,对在线协作的需求也越来越大。选择一款简单高效、免费又无广告弹窗的办公套件便非常重要。除了昂贵的微软Office,太多广告的WPS,你还有另一个选择:ONLYOFFICE。 什么是 ONLY…

论程序员提问的艺术

最近工作比较忙,加上空闲时间大部分都是在维护开发【云狗AI】,所以也有一段时间没更新视频了,有不懂的,也可以问一下【云狗AI】以后我也会花更多的时间在维护这个项目中。争取给大家带来更好的体验。 主要是因为最近没发现什么特…

播放WebRTC开源库回调出来的视频码流时遇到的内存越界问题排查

目录 1、执行memset操作时遇到了内存访问违例,导致程序崩溃 2、查看崩溃时的函数调用堆栈,初步怀疑是memset时有内存越界 3、存放YUV数据的buffer在执行若干操作后出现内存越界 4、加载系统库的pdb之后,看到了更多的函数调用堆栈&…

代码随想录二刷 day23 | 二叉树 之 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树

二刷day23 669. 修剪二叉搜索树108.将有序数组转换为二叉搜索树538.把二叉搜索树转换为累加树 669. 修剪二叉搜索树 题目链接 解题思路: 递归三部曲 确定递归函数的参数以及返回值 这里我们为什么需要返回值呢? 因为是要遍历整棵树,做修改…

SOLIDWORKS Electrical如何绘制电线

SOLIDWORKS Electrical 是一套计算机辅助工程 (CAE) 设计工具,他可以帮助电气设计工程师减少创新的内在风险,并允许公司减少对物理原型的需求,从而在更短时间内以更低成本将产品推向市场。通过一组强大、直观的电气设计功能,设计人…

互联网大佬们的成功绝非偶然

以下排名不分先后。 一、百度李彦宏 ,1987年阳泉市高考状元,考进北大。 互联网三巨头BAT的第一个字母,指的就是百度。创始人李彦宏是出生在普通家庭出生,他小时候的启蒙教育由三个姐姐负责,不过相比学习,更…

详解国产指纹芯片的工作原理以及应用领域

指纹芯片,是指内嵌指纹识别技术的芯片产品,能够片上实现指纹的图像采集、特征提取、特征比对的芯片,开发者可以方便的实现指纹识别的功能,大大降低了指纹识别行业的门槛,对指纹识别的推广具有十分积极的推动作用。 近几…

数据合规之监管关注要点暨风险防范建议

上市申报过程中证券监督部门最关注的三类问题为数据源合规、数据安全及数据使用合规,而这三类问题也是企业运营时最容易产生的问题,它们无疑应是拟上市企业数据合规治理的重中之重: 1、数据源合规之监管关注要点 1、数据信息 就获取的个人数…

RaaS(勒索软件即服务)是什么?这个模型是如何工作的?

Ransomware as a Service是一个英语术语,指的是一种商业模型,其中勒索软件开发者向感兴趣的恶意行为者提供工具,以便他们可以发起勒索软件攻击。使用者通过签约创建恶意软件即服务或加入联盟计划,并分发一系列勒索软件以换取一定比例的利润。…

字符串--字符串处理函数、向函数传递字符串

一、字符串处理函数 字符串处理函数库提供了很多有用的函数用于字符串处理操作&#xff08;如复制字符串和拼接字符串等&#xff09;以及确定字符串的长度。若要使用这些字符串处理函数&#xff0c;必须在程序的开头将头文件<string.h>包含到源头文件。 例题1&#xff1a…