作者:CVNot

链接:https://juejin.im/post/5e8844996fb9a03c6675b9d6

操作系统是什么?

操作系统是用来管理计算机硬件的软件,狭义上实现该定义的为操作系统内核;而更加宽泛的操作系统概念为根据内核对外提供了一些OS服务,比如windows的图形化界面等。

操作系统启动过程

既然操作系统是在硬盘或软盘中的程序软件,那么按照冯诺依曼体系--取指执行,我们需要将操作系统从硬盘中加载到内存中,这样CPU才可以取指执行;

2.1 第一个程序:BIOS

按下开机键后,内存(RAM)在刚上电时是空白的,所以CPU中的CS(段地址):IP(段内偏移)指针如何知道运行的第一条代码是什么呢?

段寄存器名称 含义 指向的初始地址
CS 段基址  (16位) 0xF000
IP 段内偏移(16位) 0xFFF0

计算机是在硬件层面解决这个问题,即在CPU刚上电时,将其置为16位模式,启动20根地址线(A0 ~ A19,即内存的寻址空间为20位),并将CS:IP直接置在0xF000:0x0000,即0xFFFF0地址处(计算方法为 CS<<4 + IP,CS为什么要左移四位呢?是因为一共有20位的寻址空间,这样才可以寻址到全部的内存地址空间),而该地址为ROM上BIOS程序的入口地址,这一段是硬件固化好的程序,所以计算机执行的第一段代码为BIOS;


BIOS不是操作系统,所以它最核心的任务是要将内核代码从磁盘中加载进内存,它是如何做到的呢?


BIOS程序首先在内存空间中初始化了中断向量表(用来指向相应中断号的中断程序的CS:IP地址,共1KB),而一个中断向量为4个字节(CS:16位,IP:16位),并且初始化了数据区域;

而接下来BIOS就会通过调用0x19号中断(INT)服务程序加载磁盘中第一个扇区(512Bytes)中的bootsect程序(--内核中的引导程序)到内存0x07C00处,并且jmp到该地址执行;对于素数 p,有:

//bootsect.sBOOTSEG  = 0x07c0          ! original address of boot-sector

2.2 第二个程序:bootsect.s

bootsect是操作系统的引导扇区中的代码,其主要责任是将后续的内核代码(主要是setup代码和system代码)妥善的加载到内存中,并且因为此时计算机只打开了20根地址线,所以内存寻址范围为2^20 = 1MB,所以bootsect代码首先要对内存进行规划。

SETUPLEN = 4             ! setup程序代码一共占4个扇区INITSEG  = 0x9000          ! 将bootsect代码移到0x9000(稍后会分析为什么要移)SETUPSEG = 0x9020           ! setup程序会以 0x9020 为起始地点加载SYSSEG   = 0x1000          ! system(内核)会以 0x10000 为起始地点加载SYSSIZE = 0x3000                        ! 内核模块的长度ENDSEG   = SYSSEG + SYSSIZE       ! system(内核)模块截止的位置

内存规划情况如下:


而迁移bootsect主要的原因在于system(内核)模块之后会迁移到0x00000处,如果不进行迁移,那么bootsect程序就会被覆盖,在说setup模块时会提到;

规划好内存,接下来就会把setup模块和system模块依次通过0x13中断载入到内存的相应位置中,最终效果如上图所示;

在内存中不仅需要规划程序代码内存区域,也要对一些段寄存器进行重新初始化;

这里总结一下会涉及到的段寄存器,而这些段寄存器都处在CPU中:

段寄存器名称 含义 指向的初始地址
CS 段基址  (16位) 0x9000
IP 段内偏移(16位) [go] 复制bootsect时运行到bootsect程序的位置,这样可以接着执行bootsect
SS 内核栈基址指针(16位) 0x0000(与CS合为0x90000)
SP 内核栈栈顶指针 (16位) 0xFF00 (0x9FF00)
DS 数据段寄存器(16位) 0x0000 (0x90000)

其中SS与SP初始化了一个从高地址向低地址压栈的栈结构,从而程序在接下来的指令中可以使用pop与push操作运行一些复杂的数据运算类指令。


2.3 第三个程序:setup

我们已经通过bootsect.s将setup模块加载至0x92000处,正好在bootsect.s运行结束的位置,并且system内核模块也已经加载进入内存,而setup主要作用是让操作系统设置起来。

  • 首先操作系统最主要的功能是用来管理硬件,所以setup会通过BIOS提供的中断程序加载内核(system程序)运行所需的机器系统数据,例如:光标位置(0x03)、显示页面等数据,并通过0x41、0x46中断向量所指的内存区域中获取硬盘参数表1和硬盘参数表2,而这些数据(510Bytes)放在bootsect区, 可见操作系统对于空间的利用是十分严格的;


  1. 由20位寻址转变为32位寻址(即由实模式转变为保护模式)因为20位地址空间只有1MB的寻址空间,这对于一个现代操作系统来讲着实捉襟见肘,所以要转变为保护模式;

(1)第一步我们需要关闭中断并且将system内核模块移动到0x00000处;


为什么要关中断,是因为我们会将BIOS中断体系摧毁,BIOS中断体系是不适合32位的操作系统的,所以在32位保护模式中断体系没有建立起来前中断是一直关闭的;

如何关闭中断,将CPU中的标志寄存器(EFLAGS)中的中断允许标志(IF)置0;

为什么要移动system模块,人家不是呆在0x10000处挺好的嘛?如果此时要移动,那为什么不在一开始就直接把system模块放到0x00000处呢?

因为:1)当我们移动至0x00000处时,会覆盖掉BIOS的中断向量表、BIOS数据区、中断服务程序,而这也使得BIOS中断体系被废除掉;2)让内核程序的寻址空间开始地址为0x00000,这样内核的线性地址与物理地址是相同的;3)一开始没有放在0x00000处是因为我们在加载完system模块后仍然需要使用BIOS中断服务。

(2)构建中断描述符表(IDT)和全局描述符表(GDT):IDT与BIOS中断向量表作用相同,都存储着中断处理程序的入口地址,不同的是:IDT的位置是可以改变的,其在内存中的基地址存储在CPU的IDTR寄存器中,这样给操作系统的内存规划带来了灵活性;

GDT中存储段描述符,用于找到数据段、代码段等段地址,在进程状态查询、进程切换中扮演了比较重要的角色;该表在内存中的基地址存储在CPU的GDTR寄存器中;

gdt_48:    .word   0x800       ! gdt limit=2048, 256 GDT entries //GDT的限长   .word   512+gdt,0x9    ! gdt base = 0X9xxxx //GDT表的基地址

在实模式的CS:IP中,CS中存储的是代码段基址,而在保护模式下,通过GDT确定CPU下一个指令的位置,CS中存储的是GDT表中对应的段选择符,

mov    ax,#0x0001  ! protected mode (PE) bitlmsw ax      ! This is it!jmpi 0,8     ! jmp offset 0 of segment 8 (cs)

其中0表示段内偏移,而8(1000)表示GDT表的选择子,末尾00表示当前为内核态,0表示访问的是GDT表,1表示是GDT表的第2项(从0开始计数)

gdt:       //GDT的第一项 为空    .word   0,0,0,0     ! dummy        //GDT的第二项,内核代码段描述符    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)  .word   0x0000      ! base address=0 //CS下一条指向的代码段位置 .word   0x9A00      ! code read/exec  .word   0x00C0      ! granularity=4096, 386        //GDT的第三项  内核数据段描述符 .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)  .word   0x0000      ! base address=0 .word   0x9200      ! data read/write .word   0x00C0      ! granularity=4096, 386

那计算机在保护模式下为什么要通过这种间接的方式寻找段基地址等信息呢?

是因为硬件版本兼容的考虑,CS寄存器只有16位,在20位寻址模式下,CS只要左移四位(最后四位默认为0)即可表示出20位的地址空间;而在32位地址空间下,CS需要表达的信息不仅有段基址,段限长,还需要有权限信息,而这会导致需要64位的段信息,CS表示不了这么多信息,便用作GDT表的索引,去找到该段对应的一个64位信息,并且我们可以看到GDT的每一项都是16个16进制位,即64个二进制位。

(3) 打开A20地址线,转变成32位寻址模式:


(4)为建立32位中断体系做硬件层面的准备:

setup程序对8259A(可编程中断控制器)进行重新编程,因为此时的中断控制器对应的还是BIOS中断体系,此处需要一定的单片机基础,我们可以对中断控制器进行编程,设置中断条件,中断处理函数等等......

2.4 system模块的head.s开始执行:

head.s程序的主要作用在于为操作系统第一个.c函数main.c的运行做准备工作;

2.4.1 建立页目录与页:

  • 为什么要建立页目录?主要是为了便于内核对内存进行管理,为什么要在初始地址建立页目录?是因为这样在内核中线性地址与物理地址相同,不需要通过MMU表进行从线性地址到物理地址的映射;MMU在进程内存管理中会涉及到,与Java虚拟机中的TLAB作用相似,即给各个进程分配各自的内存空间,这样就不会产生多进程的数据冲突。

  • 如何建立页目录?


在0x000000处开始的4KB内存空间建立页目录表,并将页目录的前四项指向页1、2、3、4,这样页目录表便设置完成了;接着设置页表,页表中一个页表项对应了内存空间中的一个4k的页表,然后按照从高地址向低地址写的特性,从0xFFFFFF~ 0xFFF000这个内存中最后一个4K页的地址存放到页4最后一个页表项中,以此类推,从0xFFFE00开始的4k页由页4倒数第二个页表项引用。

2.4.2 重建GDT表与IDT表

(1) 在setup.s程序执行过程中,我们废除了原有的BIOS中断机制,并重新设置了可编程中断控制器,这样硬件层面的中断已经处理完成,所以我们要在软件层面完成32位中断体系的建立--即中断描述符表(IDT)的建立与中断程序的挂接(与BIOS中断体系一致);IDT表存储在0x054AA处,即紧跟在页4后,一共有256个中断entries入口。

可以品味一下两个中断表名称的差异,中断向量,何为向量,即直接存储中断程序的IP地址;何为中断描述符表,即存储中断描述符,中断描述符中存储了该描述符对应的中断程序的32位IP(EIP)段内偏移地址,16位CS(GDT段选择符)地址,还有一些权限信息--DPL,该页是否存在于内存中(P,为虚拟内存的换页提供信息),段描述符的TYPE等信息,前两者确定了中断程序的32位地址,也侧面说明了我们为什么要重建中断体系的核心原因;而初始中断程序都是挂接在默认中断程序ignore_int上,等后续的main函数运行时再进行挂接具体函数,这样的好处在于如果使用了错误的中断号,也可以获得一个默认的提示信息反馈,知道当前系统没有提供这个中断;

(2) 在setup.s程序执行过程中,我们其实已经新建了一个GDT表,其中有两项,一项指向内核数据区,一项指向内核代码区;那我们为什么要重建呢?需要注意GDT表的位置是0x90200起,而这部分区域在之后的内存规划中是设置为缓冲区的,所以为了防止GDT表被覆盖导致CPU无法找到下一条指令的位置从而崩溃的问题,我们需要将GDT放在安全的地方--0x054B2,即紧跟在IDT表后。

完成上述设置后,内存从0x00000开始的内存空间如下所示:


2.4.3 重新设置段寄存器:

因为寻址模式和系统位数(16~32)的改变,CS中不再存储段基址,而是存储对应GDT寻址方式的段选择符,所以我们需要将SS也需要转变为栈段选择符(还是16位),而栈顶指针(SP)因为可能不与CS在同一个段中,故要改为32位的ESP;


2.4.4 跳转到main.c函数执行:

经历了前面一系列的铺垫,终于可以执行C语言函数了,因为C作为一个高级语言,它的编译运行离不开操作系统的支持。

那是如何跳转的呢?我们知道程序的运行离不开数据结构-栈,C语言实现的函数调用,本质上是封装了通过共享栈完成参数的传递、被调用函数的执行与返回结果的过程;

所以我们调用main函数也是通过push与pop完成的。


当head.s完成GDT、IDT、页表的设置后,会跳转到setup_paging执行,而后再通过往内核栈里压入调用main()函数的参数--envp、argc、argv[]和L6标号以及当前程序执行到ret(返回指令)后,需要调用的函数--main函数的地址;

为什么要通过这种方式,我们知道main的地址,直接jmp不好吗?是因为安全性的考虑,mian()函数是不应该退出的,如果退出则说明出现了异常,则会返回到L6,而在这个标号的程序处,我们可以做一些系统调用,给与用户一些提示信息,并维持进程的轮转,而不至于完全崩溃。总结

至此,计算机便算是基本启动起来,而操作系统也会在main()函数中初始化一些数据结构用来管理硬件资源、设置各种中断程序、初始化进程等等操作。

欢迎关注交流探讨!

威纶通宏开机后使用初始化宏指令_【操作系统】我们按下电脑开机键的背后发生了什么?...相关推荐

  1. 威纶通宏开机后使用初始化宏指令_计算机从摁下电源到开机是怎么工作的

    我们几乎每天都要打开电源启动机器,面对屏幕上出现的一幅幅启动画面,我们一点儿也不会感到陌生,但是,计算机在显示这些启动画面时都做了些什么工作呢? 打开计算机电源后到计算机准备接受你发出的命令之间计算机 ...

  2. 威纶通宏开机后使用初始化宏指令_你按下电脑开机键后,电脑都干了些什么?...

    各位朋友,这篇文章你一定要坚持看完,看完对电脑的启动过程和故障判断会起到很大帮助哦! 我们几乎每天都要打开电源启动机器,面对屏幕上出现的一幅幅启动画面,我们一点儿也不会感到陌生,但是,计算机在显示这些 ...

  3. 威纶通宏开机后使用初始化宏指令_维纶触摸屏常用设置与重要指令

    内容: (一)配方 (二)欧姆龙PLC标签的设定和使用 (三)一些常用宏指令 1.常用 系统参数 ■ Selection 显示当前所选择配方记录的索引编号.索引编号从 0 开始计算,若点选第一笔记录, ...

  4. 威纶通触摸屏如何编写和调用宏指令进行逻辑判断

    威纶通触摸屏如何编写和调用宏指令进行逻辑判断 如下图所示,新建一个测试项目,添加触摸屏和PLC,这里以三菱FX系列PLC进行举例说明, 如下图所示,点击工程文件-宏指令, 如下图所示,进入宏指令设置界 ...

  5. 台式计算机蓝屏代码08e,win7系统下电脑开机出现蓝屏代码0x000008e解决方法(图)

    原标题:"win7系统下电脑开机出现蓝屏代码0x000008e怎么办"相关电脑问题教程分享. - 来源:191路由网. 蓝屏是我们日常使用电脑过程中最经常遇见的故障之一了,蓝屏总是 ...

  6. c 语言开机自动播放视频,小编为你win7系统电脑开机总是自动播放音乐的操作方案...

    其实大部分的朋友都还不知道win7系统电脑开机总是自动播放音乐的问题如何解决,于是就有网友向我留言询问到win7系统电脑开机总是自动播放音乐的处理步骤,不会的朋友也不用担心,下面我就给大家讲解一下wi ...

  7. 台式计算机蓝屏代码08e,win7系统下电脑开机出现蓝屏代码0x000008e怎么办

    蓝屏是我们日常使用电脑过程中最经常遇见的故障之一了,蓝屏总是让人很是头疼,而在开机出现蓝屏错误代码0x0000008E的原因主要是内存有错误或者软件不兼容导致的,那么,该如何解决蓝屏这个问题呢?下面, ...

  8. 荣耀linux电脑开机后黑屏,开机黑屏进不了系统,教您解决电脑开机黑屏进不了系统...

    随着电脑使用的普及和使用电脑的时间越长,遇到的问题也越来越多,这不有用户却遇到了电脑开机黑屏进不了系统的情况,即现象为:开机有一声响.关机一按电源键就马上断电了,怎么解决?下面,小编就跟大家分享解决电 ...

  9. word2016开机后首次打开非常慢_终于找到了电脑开机时间长的原因了,一看就会,一招到位...

    电脑在使用一段时间后,开机时间会越来越慢,少则几十秒,多则几分钟的时间才能完全打开!真是有点伤心啊,还总是以为自己买到了假电脑或是安装了假系统. 实际上,影响电脑开机速度的只有两个因素,而且都是来自软 ...

最新文章

  1. springmvc教程--整合mybatis开发(spring+springMVC+mybatis整合开发)
  2. TiDB-新一代数据库入门介绍
  3. 速修复!这个严重的 Apache Struts RCE 漏洞补丁不完整
  4. ArcGIS 城市生活区用地适宜性评价(一)
  5. 彻底卸载VS2015
  6. android暗水印技术,基于Android的隐藏数字水印技术的研究与实现
  7. P进阶_(zip函数)
  8. github 发布静态页面
  9. [Leetcode] 382. Linked List Random Node 解题报告
  10. 王者服务器维护12.3,王者荣耀3月12日更新维护公告 3.12全部更新内容改动汇总
  11. canvas绘制太极图
  12. 2018 Google IO干货摘要及对国内Android开发者的影响
  13. ARM中的MOV指令
  14. python时间复杂度和空间复杂度是指,时间复杂度与空间复杂度
  15. 差分隐私?联邦学习?安全多方计算?它们之间是什么关系?
  16. 小飞计算器(小飞可编程复数计算器)用户手册
  17. ios王者荣耀更新服务器维护,王者荣耀苹果更新不了 苹果无法进行版本更新如何解决...
  18. android 图标弹跳动画,动效教程 | 5 分钟快速做个弹跳加载小动画
  19. WebService客户端三种调用方式整理
  20. 数据挖掘概念与技术13--BUC

热门文章

  1. Udacity机器人软件工程师课程笔记(一)-样本搜索和找回-基于漫游者号模拟器
  2. ats 5.3.2中的header-rewrite插件调研
  3. ATS中的动态回源插件stale-while-revalidate调研
  4. 《DDIA》读书笔记
  5. 搜索:广搜 词语阶梯
  6. Setting the Reply-To Header in an Email using CDONTS.NewMail Object and CDO Message
  7. 关于CSS的长度单位及颜色表示
  8. 重定向后,如何通过浏览器返回定向之前的页面?
  9. sql 优化 tips
  10. Jquery JQZoom Plugin 放大鏡效果 Two