IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 从根上理解操作系统(一) -> 正文阅读

[系统运维]从根上理解操作系统(一)

目录

操作系统启动

1 BIOS执行的主要任务

2 bootsect代码的作用

3 setup完成OS启动前的设置

4 system 模块执行

?

操作系统启动

? ? ? ? 在了解操作系统之前要有一个意识,那就是计算机的运行过程就是取指执行,什么意思呢?即计算机会根据程序计数器的值获取代码指令然后执行它。

? ? ? ? 大家有没有想过,点击电脑启动按钮的那一刻,计算机到底做了一些什么动作呢?

? ? ? ? 在计算机启动的过程中,主要有四个模块:BIOS-> boot -> setup -> system,这四段程序由前往后依次执行。

? ? ? ? 这里以linux0.11为例。按开机的时候,首先启动的就是BIOS程序,这段程序是提前固化在内存中的。CS和IP分别是段寄存器和偏移寄存器,CS左移4位+IP 就是寻址地址,在开机时,CS被赋值了0xFFFF,IP被赋值了0x0000,所以计算机执行的第一条指令就是在0xFFFF0处的指令,而BIOS程序就是固化在这里的,所以说BIOS程序可以在开机时首先被执行。

1 BIOS执行的主要任务

  1. 检查RAM、键盘、显示器、软硬磁盘等
  2. 将磁盘0磁道0扇区(引导扇区,512字节)的内容读到0x7c00处
  3. 设置cs=0x07c0,ip=0x0000

? ? ? ? 接下来计算机就会从0x7c00处执行程序,而这段程序就是从引导扇区读入的代码 bootsect.s

2 bootsect代码的作用

? ? ? ? bootsect程序是磁盘引导块程序,在磁盘的第一个扇区,主要的处理步骤如下:

  1. 将7c00处的256个字移动到90000处(留下的空间留给操作系统)
  2. 载入setup模块
  3. 读入setup后,显示字符“loading system 。。。”
  4. 读入system模块
  5. 控制权交给setup

3 setup完成OS启动前的设置

? ? ? ? 磁盘第二个扇区开始的4个扇区为setup模块,会被加载到内存紧接着bootsect后面位置处。从名字上也可以看出来,这个模块是为了完成OS启动前的参数设置。setup的主要功能如下:

  1. 硬件参数获取与设置存储
  2. 把操作系统代码移动到0地址处
  3. 进入保护模式
  4. 切换至32位机解释模式
  5. 此时cs中存的是选择子,通过查gdt表获取真正的基址
  6. 通过 jmpi 0, 8 跳到了0地址处开始执行

? ? ? ? 这里关键有一个“保护模式”。那么什么是保护模式,为什么需要保护模式?这里需要对其进行解释:

? ? ? ? 内存是指一组有序字节组成的数组,每个字节有唯一的内存地址,内存寻址则是指对存储在内存中的某个指定数据对象的地址进行定位。对于80x86CPU,一条指令主要由操作码和操作数构成,操作数可以位于一个寄存器中,也可以在内存中,若要定位内存中的操作数,就要进行内存寻址。

????????早期的8086处理器寄存器宽度只有16位,16位的寄存器只能进行 64 KB 的寻址,而 8086 有 20 根地址线,按照地址线来计算可以进行 1 MB 的寻址,所以16 位宽度的寄存器是显然不能满足需求的,为了解决这个问题,聪明的程序员想出了用?段基址:段内偏移?的方式来扩展寻址空间。

????????物理地址 = 段基址 << 4 + 段内偏移

????????CPU在访问内存前,会先经过段部件,按照上面的换算公式,计算出物理地址,这样两个 16 位的寄存器合在一起,宽度便成了 20 位。

? ? ? ? 这被称为”段“的寻址技术。这种寻址技术把内存分成了一个或多个称为段的线性区域,从而对内存中的对象的寻址就需要使用一个段的起始地址和段内偏移地址构成。

? ? ? ? 此时的寻址方式还是实模式,那么实模式有什么缺陷呢?首先就是不安全,程序可以随意访问任何物理地址。

????????为了不让某些内存数据被篡改,保护模式孕育而生!

????????cs左移4位+ip只有20位地址,只有1M的寻址空间,所以需要从16位机切换到32位机(保护模式)。保护模式用的是与实模式不同的一套电路,将cr0的PE位设置为1就切换到了保护模式。

????????在保护模式下,很重要的一点就是段寄存器不是直接存放段基址了,而是存放着段选择子

段选择子,大可以认为就是索引,用于索引段描述符。而段描述符,顾名思义,用来描述一个段的信息,长度为64位,其中有32位用来存放段基址,剩下32位存放着段界限等信息。那么段描述符存放在哪里呢?GDT,全局描述符表,全局描述符表会存放着所有的段描述符。每个段描述符的长度是8字节,含有3个主要字段:段基地址、段限长和段属性。

????????那我把索引(段选择子)告诉CPU了,他怎么知道上哪找GDT 呢?当然你得提前告诉CPU GDT的地址:

lgdt [GDT地址]

????????只要执行了上面这个指令,CPU 便会记录下GDT的地址了,将其存到GDTR寄存器中。

? ? ? ? 所以保护模式下寻址的过程如下所示

? ? ? ?通过GDR寄存器找到GDT表的位置,然后通过段选择子找到对应的段描述符,而描述符里存的有段基址,所以通过段基址和偏移地址就可以定位到需要的内存地址。这里需要预告一个概念——分页。在未开启分页的情况下,段描述符中的段基址和偏移地址组合起来就是物理内存地址:

? ? ? ? ?其实除了寻址方式的变化,保护模式还增加了一个新名词:特权级。处理器的段保护机制可以识别0~3级共4级特权级,数值越大,特权越小。主流操作系统主要使用两个特权级0和3。0代表内核级,3代表用户级。由3>0,所以用户态的权限要小于内核态,也就是用户段的程序无法直接跳到内核段取指令执行程序,这样就起到了保护的作用。那么这个特权保护功能是怎么实现的呢?

? ? ? ? 我们通过上面段描述符的格式图里可以看到有一位被称作DPL——描述符特权级,这个代表了该描述符所关联的代码段的特权级。而当前段的特权级存储在段选择子的位0和位1中。

? ? ? ? 假如有一个用户段A想跳转到内核段B执行,那么会首先判断A的CPL是否小于等于B的DPL,如果成立,则可以跳转,否则,不允许跳转。由于用户段的特权级为3,即CPL=3,内核段的特权级为0,即DPL=0,跳转条件不成立,所以不允许跳转。这样就起到了保护内核的作用。(这里还有个RPL的概念,跳转条件还需要满足DPL>=RPL,不再赘述)

? ? ? ? 总结从实模式到保护模式的步骤如下:

  1. 准备GDT
  2. 用lgdt加载gdtr
  3. 打开A20(这一步的加入是由于历史原因导致的)
  4. 置cr0的PE位
  5. 跳转,进入保护模式

? ? ? ? 那么既然用户段程序无法跳转至内核段执行内核的代码,那么用户怎么使用操作系统提供的能力呢?这里就要提到IDT了,IDT是中断向量表,跟GDT表的结构很类似,通过int指令可以在IDT表中找到中断描述符,中断描述符里也有一个DPL,而这个DPL是3,所以用户段可以执行中断入口程序,而中断入口程序的CPL是0,所以中断入口程序可以执行内核段的代码,这样,用户就可以使用操作系统提供的能力了,并且使用什么能力是由中断入口程序决定的,从而起到了保护作用。

4 system 模块执行

? ? ? ? system模块的执行主要分两步:head.s的执行,另一个是main.c的执行。

? ? ? ? head.s程序在被编译生成目标文件后会与内核其他程序一起被链接成system模块,位于system模块的最前面开始部分,这也是为什么称其为头部程序的主要原因。

?head.s:

  1. ?加载各个数据段寄存器,重新设置中断描述符表idt
  2. 重新设置全局描述符表gdt(重新设置gdt的主要原因是为了把gdt表放在内存内核代码比较合理的地方)
  3. 管理内存的分页处理机制
  4. 利用返回指令将预先放置在堆栈中的mian程序的入口地址弹出,去运行main程序

? ? ? ? ?从0地址处开始就有了操作系统,并且有了初始化好的数据结构(GDT、IDT、mem_map等)。

main.c:

? ? ? ? 该程序首先利用前面setup.s程序取得的系统参数设置系统的根文件设备号以及一些内存全局变量。这些内存全局变量指明了主内存的开始地址、系统所拥有的内存容量和作为高速缓冲区内存的末端地址

? ? ? ? 主内存区域的内存由内存管理模块mm通过分页机制进行管理分配。内核程序可以自由访问高速缓冲中的数据,但是需要通过mm才能使用分配到的内存页面。

? ? ? ? ?mian函数开始就是进行了一系列的初始化,我们以mem_init()为例来说明一下初始化的过程:

? ? ? ? ?mem_init函数其实就是初始化了一个mem_map的数组,代表整个内存空间的页,0代表对应的页没有使用,USED代表已经使用。

参考:

《linux0.11完全剖析》

李治军《操作系统》课程

从linux0.11看一个进程的诞生 - 云+社区 - 腾讯云

真棒! 20 张图揭开内存管理的迷雾,瞬间豁然开朗 - SegmentFault 思否

32位机,CPU是如何利用段寄存器寻址的 - ggzone - 博客园

hurlex <六> 添加全局段描述符表 - 哔哩哔哩

Linux历程-进程的创建

GDT,LDT,GDTR,LDTR 详解,包你理解透彻 | 技术部落

操作系统:实模式到保护模式 - 知乎

操作系统与操作系统内核 - 知乎

内核态与用户态的区别,这个写的比较好 - pipci - 博客园

进入内核态究竟是什么意思? - 知乎

进程的创建和终止(超详细)

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:50:00  更:2022-04-24 09:53:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 19:54:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码