首页 > 其他分享 >自制操作系统教程(持续更新)

自制操作系统教程(持续更新)

时间:2022-10-24 19:56:17浏览次数:76  
标签:教程 BPB 操作系统 自制 db mov 扇区 FAT12 ax

-1.写在开始之前

虽然网上此类教程云集,虽然此类书籍很多,但是!

这些书籍有很多地方讲得不够细致(主要是代码有缺漏),有些对代码的更改甚至在书中了无痕迹

而这才是我开启这篇教程的原因。

这篇教程之中,只要照着所有的操作做了一遍,以您 OIer 的水平,应当能够写出完整的操作系统

不过,与其说这篇文章是个教程,倒不如说是一个学习笔记和我自身编程经验的记录。

0.开发环境配置

如果您使用的是 Linux,我们只需要输入下面一行命令即可完成开发环境的配置:

sudo apt-get install nasm build-essential qemu-system-x86

如果您使用的 Linux 中不含有 apt 系列包管理器,请使用您系统中的包管理器。

如果您使用的是 Linux,但您的系统内没有包管理器,那么您可以去 nasm 官网、 gcc 官网和 qemu 官网下载源码,然后 configure -> make -> sudo make install。

如果您使用的是 Windows,请去以下地方获取所需要的工具:

nasm

交叉编译的gcc(请下载i686-elf-tools-windows.zip)

qemu

make

dd

bochs(其实我们只需要其中的 bximage.exe )(如果是 32 位电脑请下载 2.5 以前的版本)

edimg(这个就是《30天自制操作系统》的写盘工具)

如果您使用的是 macOS,那么请注意,系统内置的 gcc 会把文件编译成 Mach-O 格式,请通过 Homebrew 下载交叉编译器:

brew install i386-elf-binutils

brew install i386-elf-gcc

然后我们还需要去往 nasm 官网获取可执行文件,并执行:

brew install qemu

以获取 qemu。

在安装完之后,如果您使用的是 Windows,请确保它们的路径位于 PATH 下!

除此之外便没什么重点了,不过,对于下文给出的工具名称默认以 Windows 为准,若您使用 Linux,请去掉工具前缀,若您使用 macOS,请将工具前缀中的 i686 改为 i386!!!

对了,如果您使用的是 LinuxmacOS,请确保您在 dd 命令的后面加入 conv=notrunc !!!

那么,开发环境配置正式结束,征程开始!

1.第一个引导扇区

所谓引导扇区,其实就是一段可执行的代码而已,不过加入了一个小限制:编译后的总字节数不能超过 512,同时扇区最后两个字节必须是 55 AA 。

虽然现在看来这个限制并不怎样,但一到后面再回过头来,您将会发现这是一个非常恶心的限制。不过没关系,对于现在的我们来说,这个限制并不大。

那么我们的目标就是用 BIOS 中断 int 10h 往屏幕上输出信息,具体用法如下:

AH=13h:输出信息

BH=页码(一般可以置0)

BL=属性(当al=0或1)

(DH, DL):行和列

ES:BP:字符串地址

AL=输出方式

AL=0:仅含显示字符,字符属性(颜色等)位于 BL 中。显示后,光标位置不变。

AL=1:同 AL=0,但显示后光标位置改变。

AL=2:字符串中含有显示字符和显示属性。显示后,光标位置不变。

AL=3:同 AL=2,但显示后光标位置改变。

那么此次我们要使用的就是 AH=13h AL=01h 的显示方法,即显示字符串后光标移动。

知道怎么显示字符串,代码也就好写了:

代码 1-1 最简单的引导扇区(boot.asm)

    org 07c00h ; 告诉编译器程序将装载至0x7c00处

    mov ax, cs
    mov ds, ax
    mov es, ax ; 将ds es设置为cs的值(因为此时字符串存在代码段内)
    call DispStr ; 显示字符函数
    jmp $ ; 死循环

DispStr:
    mov ax, BootMessage
    mov bp, ax ; es前面设置过了,所以此处的bp就是串地址
    mov cx, 16 ; 字符串长度
    mov ax, 01301h ; 显示模式
    mov bx, 000ch ; 显示属性
    mov dl, 0 ; 显示坐标(这里只设置列因为行固定是0)
    int 10h ; 显示
    ret

BootMessage: db "Hello, OS world!"
times 510 - ($ - $$) db 0
db 0x55, 0xaa ; 确保最后两个字节是0x55AA

程序写好了,我们该怎么运行呢?首先编译一下:

nasm boot.asm -o boot.bin

对于 LinuxmacOS 用户而言,只需要下面两行命令就可以完成软盘映像的创建与写入:

dd if=/dev/zero of=a.img bs=512 count=2880
dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc

如果您使用的是 Windows,那么请执行 bximage。下面是使用 bximage 创建软盘映像的实例:

> bximage
========================================================================
                                bximage
                  Disk Image Creation Tool for Bochs
        $Id: bximage.c,v 1.34 2009/04/14 09:45:22 sshwarts Exp $
========================================================================

Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd

Choose the size of floppy disk image to create, in megabytes.
Please type 0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88. [1.44]

I will create a floppy image with
  heads=2
  sectors per track=18
  total sectors=2880
  total bytes=1474560

What should I name the image? [a.img]

Writing: [] Done.

I wrote 1474560 bytes to a.img.

The following line should appear in your bochsrc:
  floppya: image="a.img", status=inserted
(The line is stored in your windows clipborad, use CTRL-V to paste)

Press any key to continue
>

硬盘镜像制作完成之后,我们再执行一条写入命令:

dd if=boot.bin of=a.img bs=512 count=1

注意,Windows 下的 dd 不支持 conv 选项。

另外,如果您的 boot.bin 被报毒 KillMBR,请不要惊慌,因为它就是一个 MBR,因此被判为覆盖 MBR 的病毒非常正常,默认不做操作即可。

无论是上述哪种情况,在制作完成之后,直接执行一行命令来执行:

qemu-system-i386 -fda a.img

如果您的执行结果如下图,那么恭喜您,您的引导扇区成功执行了!

图 1-1 运行结果

(图 1-1 运行结果)

无论您使用的是哪种虚拟机,只要左上角出现 Hello, OS world! 就算是成功。


2.FAT12 文件系统

前面我们花了极大的篇幅来写一个极简引导扇区的实现,但是本节相比之下就要短很多了,我们要在我们的引导扇区中加入 FAT12 文件系统头,这样后续我们写入 LoaderKernel 就要方便很多了。

FAT12 文件系统的具体结构如下图所示(实在懒得打列表了,干脆搬了一张网图):

如诸位所见,FAT12 文件系统头占用了汇编程序开头的 64 个字节。注意,对于 org 指令来说,它并不在所编译得出的机器码之列,因此被称为伪指令

那么我们就依照此结构写入一下这些结构吧:

代码 2-1 FAT12 文件系统头(boot.asm)

    org 07c00h ; 告诉编译器程序将装载至0x7c00处

    jmp short LABEL_START
    nop ; BS_JMPBoot 由于要三个字节而jmp到LABEL_START只有两个字节 所以加一个nop

    BS_OEMName     db 'tutorial'    ; 固定的8个字节
    BPB_BytsPerSec dw 512           ; 每扇区固定512个字节
    BPB_SecPerClus db 1             ; 每簇固定1个扇区
    BPB_RsvdSecCnt dw 1             ; MBR固定占用1个扇区
    BPB_NumFATs    db 2             ; FAT12 文件系统固定2个 FAT 表
    BPB_RootEntCnt dw 224           ; FAT12 文件系统中根目录最大224个文件
    BPB_TotSec16   dw 2880          ; 1.44MB磁盘固定2880个扇区
    BPB_Media      db 0xF0          ; 介质描述符,固定为0xF0
    BPB_FATSz16    dw 9             ; 一个FAT表所占的扇区数,FAT12 文件系统固定为9个扇区
    BPB_SecPerTrk  dw 18            ; 每磁道扇区数,固定为18
    BPB_NumHeads   dw 2             ; 磁头数,bximage 的输出告诉我们是2个
    BPB_HiddSec    dd 0             ; 隐藏扇区数,没有
    BPB_TotSec32   dd 0             ; 若之前的 BPB_TotSec16 处没有记录扇区数,则由此记录,如果记录了,这里直接置0即可
    BS_DrvNum      db 0             ; int 13h 调用时所读取的驱动器号,由于只挂在一个软盘所以是0 
    BS_Reserved1   db 0             ; 未使用,预留
    BS_BootSig     db 29h           ; 扩展引导标记
    BS_VolID       dd 0             ; 卷序列号,由于只挂载一个软盘所以为0
    BS_VolLab      db 'OS-tutorial' ; 卷标,11个字节
    BS_FileSysType db 'FAT12   '    ; 由于是 FAT12 文件系统,所以写入 FAT12 后补齐8个字节

LABEL_START: ; 后面就是正常的引导代码
    mov ax, cs
    mov ds, ax
    mov es, ax ; 将ds es设置为cs的值(因为此时字符串存在代码段内)
    call DispStr ; 显示字符函数
    jmp $ ; 死循环

DispStr:
    mov ax, BootMessage
    mov bp, ax ; es前面设置过了,所以此处的bp就是串地址
    mov cx, 16 ; 字符串长度
    mov ax, 01301h ; 显示模式
    mov bx, 000ch ; 显示属性
    mov dl, 0 ; 显示坐标(这里只设置列因为行固定是0)
    int 10h ; 显示
    ret

BootMessage: db "Hello, OS world!"
times 510 - ($ - $$) db 0
db 0x55, 0xaa ; 确保最后两个字节是0x55AA

按上文的方法编译运行,结果仍应如图 1-1 所示。虽然显示结果没有变化,但此时的软盘已经拥有了 FAT12 文件系统。

3.查找 Loader

总是困在小小的引导扇区之中,也不是长久之计,毕竟只有 446 个字节能给我们自由支配,而保护模式的栈动不动就 512 字节,一个引导扇区完全盛不下。所以我们有必要进入一个跳板模块,并在其中进行初始化工作,再进入内核。

这时候又该有人问了:

啊所以为什么不直接进内核呢?
emmm,事实上也有这种系统(比如 haribote),但这样的一个缺点就是你的内核文件结构必须很简单甚至根本没有结构才行。

所以我们还是老老实实地跳入 Loader 再进内核吧,不过话说回来,我们现在连一个正经八百的 Loader 都还没有,不着急,我们马上创建一个:

代码 3-1 极简 Loader(loader.asm)

    org 0100h

    mov ax, 0B800h
    mov gs, ax ; 将gs设置为0xB800,即文本模式下的显存地址
    mov ah, 0Fh ; 显示属性,此处指白色
    mov al, 'L' ; 待显示的字符
    mov [gs:((80 * 0 + 39) * 2)], ax ; 直接写入显存
    
    jmp $ ; 卡死在此处

这个 Loader 的作用很简单,只是在屏幕第一行的正中央显示一个白色的 “L” 。

现在最主要的问题就是我们应该怎样寻找 Loader 呢?

这个很简单,在根目录区中是一个一个一个32字节的文件结构,其中就包含文件名,我们在根目录区中查找即可。

依照 FAT12 文件系统,根目录区排在 FAT 表和引导扇区后面,因此它的起始扇区是 BPB_RsvdSecCnt + BPB_NumFATs * BPB_FATSz16 = 19 号扇区;它的结束位置则是 19 + BPB_RootEntCnt * 32 / BPB_BytsPerSec = 33。

于是我们的思路便有了:从第 19 号扇区开始,依次读取根目录区,并在其中查找 LOADER BIN(loader.bin写入之后的文件名)。如果已经读到第 34 扇区而仍然没有找到 LOADER BIN,那么就默认该磁盘内不存在 loader

那么我们该怎么读取磁盘呢?这又是个问题,不过,int 13h 已经给我们提供了这个功能:

AH=02h:读取磁盘

AL:待读取扇区数

CH:起始扇区所在的柱面

DH:起始扇区所在的磁头

CL:起始扇区在柱面内的编号

DL:驱动器号

ES:BX:读入缓冲区的地址

返回值:

FLAGS.CF=0:操作成功,AH=0,AL=成功读入的扇区总数

FLAGS.CF=1:操作失败,AH 存放错误编码

错误编码我们并不需要,只需要保证 FLAGS.CF 的值为 0 就可以了。对此,我们可以执行一个 jc 跳转命令,它的作用是当 FLAGS.CF1 时跳转。

思路有了,读盘功能也有了,我们就开始写程序吧。首先在 DispStr 函数的后面加入一个读取扇区的函数 ReadSector,它的作用是从第 ax 号扇区开始,读取 cl 个扇区至 es:bx


4.加载并跳入 Loader

5.切换画面模式

6.进入保护模式

7.读入内核

8.重新放置内核并进入内核

9.使用 C 来编程

10.读取启动信息

11.实现最基础的打印函数

12.挑战内存分配

13.异常与中断

14.多进程

15.get_ticks 系统调用

16.键盘驱动(上)——获取按键编码

17.键盘驱动(下)——解析按键编码

18.printf 究竟是怎么实现的

19.从软盘到硬盘

20.一个简易的文件系统(上)——静态结构搭建

21.一个简易的文件系统(中)——底层操作函数

22.一个简易的文件系统(下)——配套系统调用

23.forkexitwait

24.一个自己的 shell

25.第一个应用程序

26.是时候绘制一个桌面了

27.图层管理

28.窗口管理

29.CMOS 获取当前时间的实现

30.壁纸

标签:教程,BPB,操作系统,自制,db,mov,扇区,FAT12,ax
From: https://www.cnblogs.com/yet-another-cnblogs-blog/p/16822573.html

相关文章

  • 这个项目是一个学习 LLVM 的教程
    https://github.com/P2Tree/LLVM_for_cpu0介绍这个项目是一个学习LLVM的教程,我实现了一个LLVM框架下的后端,用来编译能够在Cpu0上执行的可执行代码,Cpu0是一个简单......
  • Epson RC+ 7.0 下载安装使用图文教程
    简介爱普生机器人仿真系统,是通过计算机对实际的机器人系统进行模拟的技术。机器人仿真系统可以通过单机或者多台机器人从而组成工作站或是生产线。这些工业机器人的仿真软......
  • 操作系统之进程操作 #打卡不停更#
    (进程操作)前言进程是操作系统最重要的一个概念。对大多数操作系统内的进程能并发执行,他们可以动态创建和删除,因此操作系统必须提供某种机制以创建和终止进程。介绍进程......
  • Windows10 + Eclipse C/C++开发环境配置极简教程
    下载安装Eclipse访问下载EclipseIDEforC/C++Developershttps://www.eclipse.org/downloads/packages/将下载下来的压缩包解压到安装目录,如我这里是D:\util\cdt......
  • Vmware虚拟机上CentOS8安装教程
    这篇文章介绍如何在Vmware虚拟机上安装CentOS8操作系统,第一章节先介绍了CentOS的特点,第二章节介绍CentOS操作系统下载,虚拟机下载安装,然后开始介绍操作系统的安装过程,配置过......
  • 无需越狱,第三方微信多开聊天记录恢复教程
    无需越狱,第三方微信多开聊天记录恢复教程 未分类 3个月前 陌路  2,265 0 1多开微信每次掉签就面临一个问题,微信聊天记录保存不了,怎么去完美恢复微信的历史......
  • 大家都会的HTML基础知识-HTML教程(1)
    01、HTML基础简介HTML(HyperTextMarkupLanguage,超文本标记语言)不是一门编程语言,而是一种用于定义内容结构的标记语言,用来描述网页内容,文件格式为.html。HTML由一系......
  • Jenkins+allure+pytest 完整下载安装配置小白教程(自动化持续集成)
     具体学习视频可以参考:https://www.bilibili.com/video/BV1Bf4y1L7su/?spm_id_from=333.337.search-card.all.click&vd_source=59b7787316220625d1c23b055c444107  ......
  • 银河麒麟服务器操作系统目录、文件显示颜色的设置生效
    拷贝/etc/DIR_COLORS文件为当前主目录的.dir_colors命令:cp/etc/DIR_COLORS~/.dir_colors修改~/.dir_colors中DIR对应的颜色vim~/.dir_colors找到下面这一行:DIR01;......
  • Python安装OCR识别库tesserocr_pytesseract教程
    Python安装OCR识别库tesserocr1.tesserocr下载https://digi.bib.uni-mannheim.de/tesseract/尽量选不带dev的版本,dev是开发版本,不带dev的是稳定版个人配置tesseract-......