目录
写在前面
笔者目前大二,学过C++,汇编,操作系统,正在学习编译原理和计算机网络,打算做一个小的操作系统来巩固知识,加深对计算机的理解。
参考资料
目前正在学习《自己动手写操作系统》,资源参考一下博客:
自己动手写操作系统(高清图书+源代码)分享
开始动手
写一个最小的操作系统
一开始就遇到困难了,书中说10分钟完成一个操作系统,我花了一个小时了环境都没弄好,好在学计算机的不怕折腾,先附张结果图:
下面详述步骤:
参考文档:
步骤:
1.首先去官网下载安装NASM,配置环境变量,网址
2.下载虚拟机VirtualBox,网址(需要科学上网)
3.编写源代码,利用NASM编译成.bin文件,使用FloppyWriter.exe将文件写到TINIX.IMG文件里,经调试直接将后缀bin改成IMG好像也可以
4.新建一个虚拟机,网址,已经讲得很详细了,需要注意的是添加软盘选择IMG文件,我运行的时候试了几次才出现红色的“Hello,OS World!”,也不知道是什么原因。
这真的是太棒了,虽然你知道它有多么简陋,但是,毕竟你已经制作了一个可以引导的软盘了,而且所有工作都是你亲手独立完成的!
调试.COM
经历了一个寒假,汇编都忘得一干二净了,不得不从以前的教学文档里找回一些零星的记忆。
还好DOSBox还在,如果没有DOSBox,建议去官网下载一个,我找到了我安装Masm的地方,在旁边又把Nasm再安装了一遍(这样就不用在DOSBox\DOSBox-0.74\DOSBox 0.74 Options.bat里再配置一遍虚拟C盘了)
将boot.asm中的第一句改成org 07c00h
,输入nasm boot.asm -o boot.com
,打开DOSBox,找到boot.com运行就能看到左上角的"Hello,OS World"了
(由于jmp $
这条语句,目前应当是卡死了的)
也可以使用debug工具直接调试的
汇编课上学到的东西,终于派上用场啦!
环境配置(Linux)
用VisualBox虚拟器运行Ubuntu系统
按照书上所说,可以用虚拟器运行Linux系统,但是书上只给出了Visual PC版的安装方法,如果使用VisualBox的话就要用别的方法了
参考文档:Linux下载安装过程 官网
这两篇教程应该足够了,只是耗费的时间比较长,如果顺利的话,Tinix应该可以运行了
安装Linux系统
VisualBox虚拟器上的Linux系统虽然容易安装,但运行起来实在是太慢了,随便一个命令都要等上好几秒甚至几十秒,像打开filefox这种直接就卡死机了,特别耽误时间而且影响心态(不知道是不是我电脑的问题),总之,磨了一天多终于把Linux系统给装上了,反正迟早要装的。
参考链接:
1.新手安装 Ubuntu 操作系统步骤教程
2.Ubuntu18.04安装教程
Dell笔记本的BIOS配置
如果都能按教程的来不出错的话,安装过程将会容易得多,不知道是不是版本问题,我的BIOS和教程总有一些出入,每次教程看得一半就做不下去了,下面的博客整理了一些问题,希望大家安装顺利!
关于dell设置U盘启动与安装Ubuntu的几个问题
安装Ubuntu后需要一些配置,写给工程师的 Ubuntu 20.04 最佳配置指南
保护模式
理解实模式和保护模式
由于各种原因,保护模式这章我卡了特别久,现在来整理一下。
参考文档:
实模式与保护模式解惑之(一)——二者的起源与区别
实模式和保护模式区别及寻址方式
为了更方便的理解汇编代码,首先要清楚两种模式在寻址方式上的区别,了解开设A20线的意义(第一篇文章有提到),才能理解为什么进入保护模式前要先打开地址线A20。
(个人理解)保护模式其中“保护”的含义就是要保护系统程序,让用户程序不能随便动系统程序空间的代码,这个其实在我们操作系统课里学过,就是分段操作,不过实模式下虽然有segment的概念,由于采用 hysicaladdress=segment * 16 + offset 的模式,对保护系统程序并没有什么作用;而保护模式中通过段选择符指向段描述符,再由段描述符指向段段基址加上段内偏移地址得出线性地址(详见第二篇文章),其中段描述符的S位和TYPE字段又将相应段分为了数据段、代码段和系统段,不同段有不同的权限,从而真正体现了保护的作用。
增加一个用LDT描述的任务
不来点实战真的不知道自己在看的什么。。。
正好看到书上62面有一个“读者有兴趣可以完成小任务”,做着试试看吧。
为了不搞复杂了,就熟悉熟悉LDT的用法,我们新建一个描述符表MYLDT,考虑到汇编不好调试的问题,为了少磨点心态,我们就把显示的“L”替换成“S”,证明我们改过来了,开干!
- 增加一个32位的代码段CodeB
; CodeB (MYLDT, 32 位代码段) 自己加的LDT代码段,打印一个S
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_B:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的)
mov edi, (80 * 12 + 0) * 2 ; 屏幕第 10 行, 第 0 列。
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov al, 'S'
mov [gs:edi], ax
; 准备经由16位代码段跳回实模式
jmp SelectorCode16:0
CodeBLen equ $ - LABEL_CODE_B
; END of [SECTION .la]
- 增加描述符表
;MYLDT ;自己加的MYLDT段,内容是一个LDT描述符表,其中只有一个代码段描述符
[SECTION .ldt]
ALIGN 32
LABEL_MYLDT:
;
LABEL_MYLDT_DESC_CODEB: Descriptor 0,CodeBLen-1,DA_C+DA_32;Code,32位
MYLDTLen equ $-LABEL_MYLDT
;MYLDT选择子
SelectorMYLDTCodeB equ LABEL_MYLDT_DESC_CODEB - LABEL_MYLDT+SA_TIL
; END of [SECTION .ldt]
- 在GDT中加入MYLDT段描述符与段选择子
LABEL_DESC_MYLDT: Descriptor 0, MYLDTLen - 1, DA_LDT ;MYLDT 自己加的,用来描述MYLDT
SelectorMYLDT equ LABEL_DESC_MYLDT - LABEL_GDT ;自己加的,用来作MYLDT段选择子
- 初始化
; 初始化 MYLDT 在 GDT 中的描述符 ;自己加的,初始化MYLDT
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_MYLDT
mov word [LABEL_DESC_MYLDT + 2], ax
shr eax, 16
mov byte [LABEL_DESC_MYLDT + 4], al
mov byte [LABEL_DESC_MYLDT + 7], ah
; 初始化 MYLDT 中的描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_B
mov word [LABEL_MYLDT_DESC_CODEB + 2], ax
shr eax, 16
mov byte [LABEL_MYLDT_DESC_CODEB + 4], al
mov byte [LABEL_MYLDT_DESC_CODEB + 7], ah
- 加载MYLDT
; Load MYLDT 这里改为MYLDT
mov ax, SelectorMYLDT
lldt ax
;jmp SelectorLDTCodeA:0 ; 跳入局部任务
jmp SelectorMYLDTCodeB:0 ; 跳入局部任务
- 编译
首先还是提醒一下最好别打错字了,段选择子和段描述符标签最好复制粘贴,不细心的话编译的时候可能就要像我这样一个个改了
告诉大家一个技巧,记事本行头虽然没有行号标名,右下角的状态栏还是有的,根据错误行数的提示可以快速定位到相应行数,加快debug速度。 - 运行
可以看到红色的L变为S了,nice!
特权集
个人认为特权集是保护模式最本质待部分了,书上也讲得十分复杂,我们来理顺一下。
- CPL(Current Privilege Level) 当前执行的程序或任务的特权级 存储在CS和SS的第0位和第1位上
- DPL(Descriptor Privilege level) 表示段或者门的特权级 存储在段描述符或者门描述符的DPL中
- RPL(Requested Privilege Level) 避免低特权级应用程序访问高特权级段内的数据 通过段选择子的第0位和第1位表现
再对不同段整理一下
- 访问数据段或者调用门或者TSS:CPL,RPL<=被访问DPL
- 访问非一致代码段(不使用调用门):RPL<CPL=被访问DPL
- 访问非一致代码段(使用调用门):CPL>被访问DPL,RPL不检查
- 访问一致代码段:CPL>被访问DPL,RPL不检查