手把手教你制作一个操作系统

写在前面

笔者目前大二,学过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”,证明我们改过来了,开干!

  1. 增加一个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]
  1. 增加描述符表
;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不检查
自己动手操作系统在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用C语言和汇编语言编一个具备操作系统基本功能的操作系统框架。本书不同于其他的理论型书籍,而是提供给读者一个动手实践的路线图。书中讲解了大量在开发操作系统中需注意的细节问题,这些细节不仅能使读者更深刻地认识操作系统的核心原理,而且使整个开发过程少走弯路。全书共分7章。 第1章 马上动手一个最小的“操作系统”1 1.1 准备工作1 1.2 10分钟完成的操作系统1 1.3 Boot Sector3 1.4 代码解释3 1.5 水面下的冰山5 1.6 回顾6 第2章 搭建你的工作环境7 2.1 虚拟计算机(Virtual PC)7 2.1.1 Virtual PC初体验8 2.1.2 创建你的第一个Virtual PC9 2.1.3 虚拟软盘研究12 2.1.4 虚拟软盘实战14 2.2 编译器(NASM & GCC)18 2.3 安装虚拟Linux19 2.4 在虚拟Linux上访问Windows文件夹26 2.5 安装虚拟PCDOS26 2.6 其他要素29 2.7 Bochs29 2.7.1 Bochs vs. Virtual PC vs. VMware30 2.7.2 Bochs的使用方法31 2.7.3 用Bochs进行调试33 2.7.4 在Linux上开发34 2.8 总结与回顾36 第3章 保护模式(Protect Mode)37 3.1 认识保护模式37 3.1.1 GDT(Global Des criptor Table) 42 3.1.2 实模式到保护模式,不一般的jmp45 3.1.3 描述符属性47 3.2 保护模式进阶50 3.2.1 海阔凭鱼跃50 3.2.2 LDT(Local Des criptor Table)58 3.2.3 特权级62 3.3 页式存储82 3.3.1 分页机制概述83 3.3.2 编代码启动分页机制84 3.3.3 PDE和PTE85 3.3.4 cr388 3.3.5 回头看代码88 3.3.6 克勤克俭用内存90 3.3.7 进一步体会分页机制100 3.4 中断和异常107 3.4.1 中断和异常机制109 3.4.2 外部中断111 3.4.3 编程操作8259A113 3.4.4 建立IDT116 3.4.5 实现一个中断117 3.4.6 时钟中断试验119 3.4.7 几点额外说明121 3.5 保护模式下的I/O122 3.5.1 IOPL122 3.5.2 I/O许可位图(I/O Permission Bitmap)123 3.6 保护模式小结123 第4章 让操作系统走进保护模式125 4.1 突破512字节的限制125 4.1.1 FAT12126 4.1.2 DOS可以识别的引导盘131 4.1.3 一个最简单的Loader132 4.1.4 加载Loader入内存133 4.1.5 向Loader交出控制权142 4.1.6 整理boot.asm142 4.2 保护模式下的“操作系统”144 第5章 内核雏形146 5.1 用NASM在Linux下Hello World146 5.2 再进一步,汇编和C同步使用148 5.3 ELF(Executable and Linkable Format)150 5.4 从Loader到内核155 5.4.1 用Loader加载ELF155 5.4.2 跳入保护模式161 5.4.3 重新放置内核170 5.4.4 向内核交出控制权175 5.4.5 操作系统的调试方法176 5.5 扩充内核184 5.5.1 切换堆栈和GDT184 5.5.2 整理我们的文件夹191 5.5.3 Makefile191 5.5.4 添加中断处理200 5.5.5 两点说明218 5.6 小结219 第6章 进程221 6.1 迟到的进程221 6.2 概述222 6.2.1 进程介绍222 6.2.2 未雨绸缪——形成进程的必要考虑222 6.2.3 参考的代码224 6.3 最简单的进程224 6.3.1 简单进程的关键技术预测225 6.3.2 第一步——ring0→ring1227 6.3.3 第二步——丰富中断处理程序243 6.3.4 进程体设计技巧254 6.4 多进程256 6.4.1 添加一个进程体256 6.4.2 相关的变量和宏257 6.4.3 进程表初始化代码扩充258 6.4.4 LDT260 6.4.5 修改中断处理程序261 6.4.6 添加一个任务的步骤总结263 6.4.7 号外:Minix的中断处理265 6.4.8 代码回顾与整理269 6.5 系统调用280 6.5.1 实现一个简单的系统调用280 6.5.2 get_ticks的应用286 6.6 进程调度292 6.6.1 避免对称——进程的节奏感292 6.6.2 优先级调度总结300 第7章 输入/输出系统302 7.1 键盘302 7.1.1 从中断开始——键盘初体验302 7.1.2 AT、PS/2键盘304 7.1.3 键盘敲击的过程304 7.1.4 解析扫描码309 7.2 显示器325 7.2.1 初识TTY325 7.2.2 基本概念326 7.2.3 寄存器328 7.3 TTY任务332 7.3.1 TTY任务框架的搭建334 7.3.2 多控制台340 7.3.3 完善键盘处理346 7.3.4 TTY任务总结354 7.4 区分任务和用户进程354 7.5 printf357 7.5.1 为进程指定TTY357 7.5.2 printf()的实现358 7.5.3 系统调用write()361 7.5.4 使用printf()363 后记366 参考文献369 附录书中的章节和代码对照表370
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值