2021.1.23

开始写操作系统

好奇心是动力的源泉,追究问题的本质是优秀黑客的必备素质,只有充分掌握了系统原理,才能在技术上游刃有余,才能有真正的创新和发展。中国需要更多真正的黑客,也希望更多的程序员能享受属于黑客的创造乐趣。
创造·自由·开源

实践–遇到问题–解决问题–再实践


来自闲话Linux系统安全(一)——自主访问控制(DAC),挺好的一段话
我们口中的操作系统,一般指的是:一个操作系统核心+各种扩展应用程序。但从专业的角度来讲,操作系统就是那个核心(通常称之为内核),就是将底层硬件进行抽象和虚拟化,并向使用者提供各种功能接口的软件程序。这是一种非常特殊的软件程序,它的特殊之处就在于:操作系统是使用者运行其他应用程序的底层软件基础,也是硬件功能被集中管理和调用的统一接口。它向上层隐藏了硬件结构的丑陋和不易操作,使得使用者在使用计算机时变得更加简单;向下层屏蔽了底层硬件无法理解的使用者发出的复杂指令,将其翻译成二进制序列,使得硬件可以更加快捷的予以执行。

Linux内核,从它诞生的那天开始,变表现出极强的生命力,源自于UNIX的很多的哲学思想被沿用在Linux中。一切皆文件!多么简单但却又复杂的一句话。说它简单,是因为对于用户来讲,每一个系统的资源,包括各种硬件(磁盘、内存、网络等)和软件都是以一种可访问甚至是可编辑可修改的文件的方式来展现,用户不必再去思考底层的技术了;说它复杂,是因为这种抽象和虚拟是在大量的驱动程序和数以百计的接口函数的支撑下得以完成的。

在这样的一套系统中,使用者可以用自己掌握的自然语言向计算机发号施令,而计算机也会非常忠诚地予以执行,无论成功与否。这种简化给使用者带来了极大的便利,但也同时会给计算机带来很多的安全问题。比如说:谁能在什么时间对什么文件进行什么操作?结果如何,成功还是失败?如果成功会有怎样的影响?如果失败,是否会予以记录?……等等

2021.2.3

  1. 操作系统是从加电自检时,确定内核态的核心地位的
  2. Nothing replaces your hardworking
  3. diskpart命令创建vhd虚拟磁盘文件
  4. .asm文件是汇编程序源文件,可以通过debug工具汇编成.obj文件,然后用link工具连接成.exe 文件
  5. 基本输入输出系统BIOS是个人电脑启动时加载的第一个软件
    第一个操作系统

2021.2.17

  1. windows的代码有几千万行,而我们所学的是它的kernel(内核),如何控制和管理系统的资源
  2. 数据+运算:改变寄存器的状态,从而改变cpu的运算规则,内存是存储数据的
  3. 8086有8个16位通用寄存器:AX,BX,CX,DX,SI,DI,BP,SP
  4. x类型的寄存器可以拆成两个8位的寄存器来用AH,AL,BH,BL,CH,CL,DH,DL
  5. 代码段和数据段存储在内存中,代码段中存放运算的指令,数据段存放运算的数据,CS存储代码段的开始地址,用DS指向数据段的开始位置

2021.2.26

  1. cs寄存器指向所要运行的程序段开始的位置
  2. ds寄存器指向所要运行的数据段开始的位置
  3. 这样可以让程序顺序执行,段+偏移可以灵活的加载多道程序(类似计算网络 网段号+主机号的索引思想)
  4. POST(Power On Self Test),加电自检,在操作系统运行之前 ,是固化在主板的BIOS在管理计算机,BIOS是被硬件强制加载的,加载的位置和内容是约定的
  1. 将非易失性介质内的程序读入内存
  2. cpu指向要运行的程序
  1. 人为赋予二进制数字意义,依靠软硬件进行功能实现,代码段和数据段都是逻辑上的,本质存储的都是二进制代码
  2. bios中的可用区很小,程序运行采用 引导+内核
  3. 显存区的8位为KRGBIRGB分别表示前景色和背景色
  4. BIOS在内存中是有布局的,必须在使用时进行保护,不能破坏
CS代码段寄存器
DS数据段寄存器
SS栈段寄存器
ES附加寄存器
FS附加寄存器
GS附加寄存器
  1. IP寄存器不可改变,进行自加操作
  2. 通用寄存器:AX BX CX DX SI BI BP SP
  3. 惯用法:
  • cx用作循环次数的控制
  • bx用来存储起始位置
  • si 寄存器作为复制指令的源地址
  1. 寻址方式
  1. 立即数寻址(立即数不能直接赋值给段寄存器)
    mov ax,0x18
    mov ds,ax
  2. 内存寻址
    mov ax,[fs:0x1234]
  3. 基址寻址:在操作数中,用bx寄存器作为地址的开始
  1. 磁盘引导
  1. 先选择通道,往该通道上的sector count寄存器,写入待操作的扇区数
  2. 往该通道的3个LBA寄存器吸入扇区的起始地址
  3. 往device寄存器写入LBA的24-27位,设为LBA,设置dev,表明是从盘还是主盘
  4. 往commad寄存器写命令
  5. 检查status寄存器·
  6. 读入数据

在这里插入图片描述

2021.3.1

  1. nasm.exe命令不能单独使用必须放在安装目录下进行使用
  2. vhd写入失败bug
  3. 写入成功的样子在这里插入图片描述
  4. p16视频有错误,应该使用dd命令把mdr.bin写入vhd文件,而不是loader文件
  5. 视频p16完成,撒花!!!在这里插入图片描述
  6. 计算机运行过程
  • 加电自检
  • BIOS引导
  • 与BIOS交互
  • 进行硬盘读取
  1. 计算机保护模式:16位的寄存器被扩展成32位
    在这里插入图片描述

  2. 改变寄存器的状态实际就是改变cpu的动作

  3. 向下兼容,原来的段+偏移的寻址机制,inter使用GDT全局描述符表这种数据结构来描述,并使用一个叫做GDTR的寄存器去指向

  4. 将一个对数据的访问分为三个部分,然后由cpu将其拼起来,这个过程叫保护模式下的寻址

  5. 实模式

  • CPU复位(reset)或加电(power on)的时候以实模式启动,处理器以实模式工作。
  • 在实模式下,内存寻址方式,由16位段寄存器的内容乘以16(10H)当做段基地址,加上16位偏移地址形成20位的物理地址,最大寻址空间1MB,最大分段64KB。
  • 在实模式下,所有的段都是可以读、写和可执行的。
  1. 16位实模式下,物理地址和逻辑地址相同
  2. 32位保护模式,特指cpu可以使用32位的保护模式下的寻址方式,让每一个应用程序都逻辑上(感觉上)拥有独立的4G空间。1.使用硬件MMU映射到物理地址进行地址转换2.编址的概念
  3. 将16位实模式下的段基址加偏移的直接访问物理内存的方式,改为通过硬件进行保护,只有段选择子和段描述符结合才能真正的访问内存,隔离内存的级别和cpu的工作状态,并且使用数据结构GDT(全局描述符)进行管理,硬件上使用GDTR(全局描述符寄存器进行管理)
  4. 保护模式是80386的一个革命性的寻址变化

2021.4.6

  1. 实模式下可以任意访问内存,访问的逻辑地址就是物理地址,做不到应用程序和系统程序的内存隔离
  2. 段的问题:16位实模式下物理地址=逻辑地址
  3. 逻辑地址向线性地址转换的平坦模型
    在保护模式下,32位环境下用一个段就对0到4G内存的线性访问,能直接访问内存空间,不用再进行段基址的来回切换。沟壑成坦途,在程序分段的基础上由操作系统统筹管理,解放生产力。
  4. 保护模式下要对平坦模型的寻址进行限定,在定义程序的段和段界限中要附加段的特权和类型,防止出现对内存的误操作
  5. 操作系统程序必须常驻在物理内存中,但是实模式可能出现对内存操作系统程序的破坏,所以保护模式进行了机制的修改。cs和ds所谓的16位实模式下的段寄存器不再是段基址的概念,而变成了段选择子但是不能直接访问物理地址只有和段描述符组合才能真正访问物理地址,这是硬件提供的保护机制,并且使用一个GDT(全局描述符表,使用GDTR全局描述寄存器控制 )的数据结构进行管理
  6. 操作系统可以控制和管理系统中的所有软硬件资源,但是应用程序只能访问自己的数据和代码段
  7. PUSH指令,每次压栈一个字(16位),堆栈指针sp减2(指针上移2个单元)
  8. 若OS是一个多用户和多任务的操作系统,在实模式下不设防的内存访问有可能造成程序崩溃,保护模式下每个段必须进行注册,并具有段的描述字,记录在全局描述符表Global Descriptor Table,为整个软硬件服务,并且必须在进入保护模式前进行定义,所以保护模式下可以使用GDT约束应用程序访问内存的权限

在这里插入图片描述
DPL表示CPU工作在特权级还是用户级(0-3)
S字节中0表示系统段,1表示代码段或数据段
G字节中0表示以字节为单位,1表示以4kb为单位
L是一个64位的代码段标志
TYPE表示描述符的子类型:4位组合表示代码段或数据段的可读、可写或可执行

  1. 进行内存访问前,必须先在GDT中进行描述符定义,并且它是由OS根据程序结构在加载时自己建立的,并且用户程序无法建立和修改GDT

2021.4.7

  1. xor进行异或清零
  2. EQU伪操作是将将后面的表达式赋值给前面的标号,EQU的引入提高了程序的可读性,也使其容易修改。
  3. 每个进程可能有相同的逻辑地址,但是会被映射到不同的物理地址上
  4. org address 告诉汇编器,把所有对内存的地址引用全都加上address ,提前规划运行的内存起始地址
  5. 实模式切换到保护模式
    初始化GDT
    通过lgdt进行加载
    关闭A20地址线
    打开CR0寄存器
    进入保护模式

2021.4.8

  1. GDT必须按手册的规定位进行赋值,这样才能让硬件识别,从而运行

  2. 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成
    在这里插入图片描述

  3. 在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,设计者使用GDTR存放GDT的入口地址

  4. 局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。

  5. LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中

  6. .SECTION用于定义内存的段

  7. ALIGN将下一个变量对齐到规定的边界

  8. BITS指令指定NASM产生的代码是被设计运行在16位模式的处理器上还是运行在32位模式的处理器上

  9. LLDT 加载局部描述符
    在这里插入图片描述

  10. DPL表示段的特权级,当前代码段区检查对应的真实地址时,DPL会检查程序间的允许级别是否一致,DPL较小的段有更高的访问权限,如果低权限去访问高权限则会崩溃

  11. 当内核代码段写入内存后,后续的用户程序代码段的DPL肯定比内核代码段的大,从而防止内核被覆盖导致系统崩溃,实现内存隔离

  12. TSS Task Stack Segment(任务状态段)要维护栈的结构,有三个(从ring0到ring4)权限状态转化堆栈,是cpu硬件原生的系统级别的数据结构

  13. TSS、LDT和GDT是受到硬件支持的系统级数据结构

  14. 程序员写的只是用户态下面的半成品的程序,加载运行由操作系统的内核提供

  15. TSS使用TR寄存器进行维护,这种硬件级别的数据结构,由软件进行填写,最后使用硬件实现

  16. CPL:Current Privilege Level
    DPL:Descriptor Privilege Level
    RPL:Request Privilege Level

  17. 应用程序的可信级别低,操作系统的可信级别高,当较低权限的任务需要使用高权限才能进行处理时,则需要操作系统提升该任务的权限

  18. 用户程序调用内核功能:
    1.设计两个相互跳转的段
    2.将这两个段注册到GDT中
    3.设计一个门描述符
    4.把门描述符写到GDT中(门描述符反映了两个段的跳转关系)

  19. 操作系统主要做的就是三件事:造表,填表,查表

  20. IDTR中断描述符表寄存器

  21. win系统有任务门、陷阱门、调用门、中断门

2021.4.9

  1. 设备对于CPU是透明的,CPU只去管理中断芯片8259A
  2. 中断存储通常需要在最后加一个时延,保证数据的传输完成
  3. $ 表示当前行被汇编后的地址
    $$ 表示一个节的开始处被汇编后的地址
  4. 操作系统中使用GDT、LDT和IDT进行内存的规划,然后进行造表、填表和查表进行操作
  5. 使用C的语法结构进行内核的编写,并且也没有C库可以使用,但是相比于汇编仍然可以提高开发效率

2021.4.12

  1. 搭建ubuntu系统
    在这里插入图片描述

  2. MBR只有512个字节,不够运行操作系统,那么将MBR的引导权交予loader,让loader可以读取硬盘中的多个扇区,突破512字节的内存限制,从而将操作系统内核加载到内存中

  3. Loader的作用:加载内核,打开保护模式,为系统启动准备好软硬件环境

  4. 用c进行内核的编写,不是使用c库,而是使用c的语法结构

  5. 不使用c库打印helloworld,表明c库在操作系统之上
    在这里插入图片描述

  6. 内核加载过程
    在这里插入图片描述

  7. 内核中前512个字节是MBR,是硬件自动加载的

2021.4.15

  1. 在C语言和汇编语言下的联合编程
    在这里插入图片描述
  2. 操作系统的内核实践就是一个死循环,不停的接收外围请求
  3. ld命令是二进制工具集GNU Binutils的一员,是GNU链接器,用于将目标文件与库链接为可执行程序或库文件

2021.4.16

  1. Boot进行家加电自检,mbr接管BIOS,然后loader进行kernel加载的准备
  2. asm程序是基于BIOS的中断调用,进行BIOS的IO控制,并封装后向c程序提供函数
    在这里插入图片描述
  3. PUSHA通常用于中断的保存现场(寄存器的状态)

2021.4.17

  1. dd命令中起始地址seek=blocks:从输出文件开头跳过blocks个块后再开始复制
  2. dd命令中占用块数count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数
  3. dd命令中bs=bytes:同时设置读入/输出的块大小为bytes个字节
  4. 视频28运行不出来,这个bug我一度怀疑是bochs版本的问题,最后找到了2.6.1(与视频一样的版本),发现实质是磁盘写入命令的错误,视频可能删去debug的片段,以打印出OKMBR的那个虚机vhd为基础,再使用以下两个命令写入
    dd if=mbr.bin of=dingst.vhd bs=512 count=1d
    dd if=Loader.bin of=dingst.vhd bs=512 count=1 seek=2(视频中的count=2不行,括号内的不写入命令行)
    dd if=kernel.bin of=dingst.vhd bs=512 count=1 seek=9
  5. 完成从MBR到Loader再加载kernel的基本过程
    在这里插入图片描述
  6. 每次vhd磁盘都要从MBR到Loader和kernel进行写入
    在这里插入图片描述

2021.4.19

  1. INT 16H是键盘I/O中断有0,1,2三个功能号,并存储在AH中

0号功能调用
格式:

  • MOV AH, 0
  • INT 16H

功能:执行时,一旦捕获键盘输入,字符的ASCII码放入AL中。若AL=0,则AH为输入的扩展码。


1号功能调用
格式:

  • MOV AH, 01H
  • INT 16H

功能:用来查询键盘缓冲区并设置ZF标志。ZF=1表示无键按下;ZF=0表示有键按下,且AX=键值代码(同AH=0功能)


2号功能调用
格式:

  • MOV AH, 02H
  • INT 16H

功能:检查键盘上各特殊功能键的状态。AL从高位到低位依次为Ins、Caps Lock、Num Lock、Scroll Lock、Alt、Ctrl、左Shift、右Shift各键的按下标志位,按下时,相应位为1。

2021.4.20

  1. 操作系统内建的程序,是写在操作系统内核里面的程序
  2. 阅读《操作系统 真象还原》

2021.4.21

  1. 没写满的扇区会用0进行填充
  2. 跳入保护模式,并将保护模式的二进制代码直接加载到0x9000处并执行
  3. 之前一直无法将kernel的内容打出,原来是Loader的跳转扇区没改,所以一直打印的是OKMBR
  4. 成功打印一个操作系统内核,之前是因为Loader的跳转扇区和页面错误在这里插入图片描述
  5. 结语

OS is a complex system; putting a programming layer ontop of the APl doesn’t eliminate the complexity——it merelyhides it. Sooner or later that complexity is going to jumpout and bite you in the leg. So ….….

  1. 后续测试中出现两个bug,1. help后两个enter出现cpu段溢出 2. drawpic进入后无法正常退出

2021.4.24

  1. 两种虚拟机: Virtual box,Bochs.
  2. Bochs可以用来方便的调试操作系统,你可以用它看看一台计算机从加电开始,都执行了哪些指令,各种寄存器的值怎么改变的,例如你怀疑自己的内核有问题,那么可以在内核的入口处设置一个断点,虚拟的计算机启动后,到内核的入口用就停住,你可以看看各个值是否正确。
  3. 两种编译工具: nasm,gcc nasm是开源的汇编语言编译器, windows和linux下都有相应的版本,gcc就不用说了,linux下经典的编译工具,对这两种编译工具,整个过程也只使用了它们最基本的功能。
  4. 若干个小工具: dosbox、notepad、vim等等

2021.5.5

  1. CS存放的是代码段的段基址,复位后代码段的起始地址是FFFFH
  2. BIOS(基本输入输出系统),BIOS是直接与硬件打交道的底层代码,它为操作系统提供了控制硬件设备的基本功能
  3. 0~FFFFFH的低端1MB内存非常特殊,因为最初的8086处理器能够访问的内存最大只有1MB,这1MB的低端640KB被称为基本内存,而A0000H~BFFFFH要保留给显示卡的显存使用···
    在这里插入图片描述
  4. 计算机启动过程
  1. 加电自检POST(Power-on self test),主要负责检测系统外围关键设备(如:CPU、内存、显卡、I/O、键盘鼠标等)是否正常。例如,最常见的是内存松动的情况,BIOS自检阶段会报错,系统就无法启动起来;
  2. 加电自检成功后,BIOS会读取硬盘驱动器的第一个扇区(MBR,512字节)到内存中,然后执行里面的代码(此时计算机系统的控制权转交给MBR)
  3. MBR会初始化段寄存器,然后加载硬盘第二个扇区的内核加载程序Loader到内存中,之后跳转到该扇区的内核加载程序Loader进行执行
  4. 将指定扇区的操作系统内核程序Loader从硬盘加载到内存中,并跳转到内核代码区域,将计算机的控制权交给操作系统内核
    5.操作系统内核程序监视I/O设备缓冲区的输入,通过键入相关的指令可以进行相关程序的运行
  • 按下电源开关时,计算机主板和其他设备供电,BIOS的控制芯片组会向CPU发出并保持一个RESET(重置)信号,重置CPU内部状态,直到芯片组检测到电源已经开始稳定供电后,便撤去RESET信号

  • CPU马上就从地址FFFF0H处(BIOS在16位实模式下执行,最大寻址范围是1MB)开始执行指令,这个地址存放一条跳转指令,跳到系统BIOS中真正的启动代码处

  • CPU读取ROM的BIOS代码,运行bootblock, 由于ROM的执行速度远比RAM要低,所以将BIOS代码 shadow覆盖到memory内存中去,这样可以减缩POST加电自检的时间

  • boot block 是CPU在BIOS上固化的最小指令集代码,用户无法对其修改和删除。这段代码在CPU复位后首先被运行,其功能主要是判断运行哪个存储器上的程序、检查用户代码是否有效、判断芯片是否被加密、芯片的在应用以及在系统编程功能

  • Boot,意思为“引导”,是计算机系统加电复位后CPU的第一个机器动作

  • Load,意思为“加载”,是从低速非易失性存储器ROM中“搬运”一些数据到高速易失性存储器RAM中

  • Shadow RAM也称为"影子内存",是为了提高计算机系统效率而采用的一种专门技术,所使用的物理芯片仍然是CMOS DRAM(动态随机存取存储器,参阅本书后面的内容)芯片。Shadow RAM占据了系统主存的一部分地址空间。其编址范围为C0000~FFFFF,即为1MB主存中的768KB~1024KB区域。这个区域通常也称为内存保留区,用户程序不能直接访问。Shadow RAM的功能就是是用来存放各种ROM BIOS的内容。也就是复制的ROM BIOS内容,因而又它称为ROM Shadow,这与Shadow RAM的意思一样,指得是ROM BIOS的"影子"。现在的计算机系统,只要一加电开机,BIOS信息就会被装载到Shadow RAM中的指定区域里。由于Shadow RAM的物理编址与对应的ROM相同,所以当需要访问BIOS时,只需访问Shadow RAM而不必再访问ROM,这就能大大加快计算机系统的运算时间。通常访问ROM的时间在200ns左右,访问DRAM的时间小于100ns、60ns,甚至更短。

2021.5.7

  1. LBA(Logical Block Addressing)逻辑块寻址模式,指计算机在与硬盘通信的一种模式,可以将管理的硬盘空间 从512kb扩充到8.4GB
  2. 多任务的保护模式的实现
    在这里插入图片描述
2022.1.20
  1. 完成《操作系统 真相还原》的MBR制作
参考资料

[1] 自己动手写操作系统
[2]《ORANGE’S:一个操作系统的实现》
[3] 平坦模型和分段模型
[4] Linux dd 命令
[5] Bios 是怎样被载入内存的?
[6] 《操作系统 真相还原》
[5] 计算机启动过程

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐