admin管理员组

文章数量:1561026

译至: http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf

上一篇: 从零开始的操作系统 (第二章:计算机体系结构和引导过程)

即使提供了示例代码,您无疑会发现在二进制编辑器中编写机器代码简直丧心病狂。 您必须记住或引用机器代码来使CPU工作。 幸运的是,并不是你一个人感到困扰,因此才有人编写了汇编程序,将更人性化的指令转换为特定CPU的机器代码。
在本章中,我们将尝试编写一些更复杂的引导扇区程序,以熟悉程序集以及我们的操作系统将运行的环境。

3.1 再探引导扇区

现在,我们将使用汇编语言重新创建二进制编辑的引导扇区,这样我们才能更好的欣赏这些底层语言。
我们可以将它汇编语言编译成实际的机器代码(我们的CPU可以解释为指令的字节序列):

$nasm boot_sect.asm -f bin -o boot sect.bin

其中boot_sect.asm是保存源代码的文件,如图3.1所示,引导sect.bin是我们可以在磁盘上作为引导扇区安装的组装机器代码。
请注意,我们使用-f bin选项指示nasm生成原始机器代码,而不是具有其他元信息的代码包,以便在更典型的操作系统环境中编程时可以和程序进行链接。 除了底层BIOS例程,我们现在是这台计算机上运行的唯一软件。 我们现在是操作系统,虽然现在我们的操作系统只有一个无限循环 - 但我们很快就会建立起来。

我们可以通过运行Bochs来方便地测试这个程序,而不是将其保存到软盘的引导扇区并重启我们的机器:

$bochs

或者,我们也可以使用QEmu,如下所示:

$qemu boot_sect.bin

或者,您可以将映像文件加载到虚拟化软件中,或将其写入某些可启动媒体并从真实计算机启动。 请注意,当您将镜像文件写入某个可启动媒体时,并将文件添加到媒体的文件系统中:而是您必须使用适当的工具从底层直接写入媒体(例如直接写入 磁盘的扇区)。

如果我们想更准确地看到汇编程序创建的字节数,我们可以运行以下命令,该命令以易于阅读的十六进制格式显示文件的二进制内容:

$od -t x1 -A n boot sect.bin

这条命令的输出大家应该会很熟悉。
恭喜,您刚刚用汇编语言编写了引导扇区。 正如我们将要看到的,所有操作系统必须以这种方式启动,然后将自己拉入更高级别的抽象(例如更高级别的语言,例如C / C ++)

3.2 16位实模式

CPU制造商必须竭尽全力保持其CPU(即其特定指令集)与早期CPU兼容,以便旧版软件,特别是较旧的操作系统仍可在最现代的CPU上运行。
英特尔和兼容CPU实施的解决方案是模拟该系列中最老的CPU:Intel 8086,它支持16位指令而没有内存保护概念:内存保护对于现代操作系统的稳定至关重要,因为 它可以让操作系统有能力来限制用户的进程访问,比如内核内存,防止这样的进程绕过安全机制甚至关闭整个系统。
因此,为了向后兼容,最重要的是CPU最初以16位实模式启动,随后根据现代操作系统明确要求下切换到更高级的32位(或64位)保护模式,但允许较旧的操作系统 继续运行在16为实模式下,幸福地不知道他们在现代CPU上运行。 稍后,我们将详细介绍从16位实模式到32位保护模式的这一重要步骤。
通常,当我们说CPU是16位时,我们的意思是它的指令一次执行16位的运算,例如:16位CPU将有一个特殊指令将两个16位数 在一个CPU周期相加; 如果一个进程需要将两个32位数字加在一起,则需要更多的周期,即使用16位加法。

首先我们将探索这个16位实模式环境,因为所有操作系统必须从这里开始,然后我们将看到如何切换到32位保护模式以及这样做的主要好处。

3.3呃,你好?

现在我们将编写一个看似简单的引导扇区程序,在屏幕上打印一条短消息。 要做到这一点,我们必须学习一些关于CPU如何工作的基础知识以及如何使用BIOS来帮助我们操作屏幕设备。

首先,让我们考虑一下我们在这里要做的事情。 我们想在屏幕上打印一个字符,但我们不知道如何与屏幕设备进行通信,因为可能有许多不同类型的屏幕设备,它们可能有不同的界面。 这就是我们需要使用BIOS的原因,因为BIOS已经对硬件进行了一些自动检测,显然BIOS先前在屏幕上打印了关于自检等信息,因此可以为我们提供帮助。

那么,接下来,我们要求BIOS为我们打印一个字符,但我们如何让BIOS这样做呢? 没有用于打印到屏幕的Java库 - 你在做梦。 但是,我们可以肯定的是,在计算机内存中的某个位置会有一些知道如何写入屏幕的BIOS机器代码。 事实上,我们可能会在内存中找到BIOS代码并以某种方式执行它,但是这太过复杂,而且不同机器上的BIOS代码不一样。

在这里,我们可以利用计算机的基本机制:中断。

3.3.1中断

中断是一种机制,它允许CPU暂时停止正在执行的操作,并在返回原始任务之前运行其他一些优先级较高的指令。 你可以通过软件指令(例如,int 0x10)或者需要高优先级动作例如某些硬件设备(例如,从网络设备读取一些输入数据)来引发中断。

每个中断由唯一编号表示,该编号是中断向量的索引,该中断向量表最初由BIOS在存储器开始时(即物理地址0x0)设置,该表包含中断服务程序(ISR)的地址指针。ISR只是一系列机器指令,与我们的引导扇区代码非常相似,它处理特定的中断(例如,可能从磁盘驱动器或从网卡读取新数据)。

因此,简而言之,BIOS将一些自己的ISR添加到专门用于计算机某些方面的中断向量中,例如:中断0x10导致调用与屏幕相关的ISR; 和中断0x13,磁盘相关的I / O ISR。

3.3.2 CPU寄存器

就像我们在更高级别的语言中使用变量一样,我们同样需要在操作系统中保存一些临时数据。 所有x86 CPU都有四个通用寄存器ax,bx,cx和dx,正是出于这个目的。 此外,与访问主存储器相比,这些寄存器每个可以保存一个字(两个字节,16位)数据,可以由CPU读取和写入,延迟可以忽略不计。 在汇编程序中,最常见的操作之一是在这些寄存器之间移动(或更准确地说,复制)数据:

mov ax, 1234          ; 在AX寄存器中保存数字1234
mov cx, 0x234         ; 保存16进制数 0x234 到 CX寄存器
mov dx, ’t’           ; 保存ASCLL码  ’t’ in DX寄存器
mov bx, ax            ; 将 AX 寄存器的值复制到 BX寄存器,现在BX == 1234

请注意,目标是mov操作的第一个参数而不是第二个参数,但此规定因不同的汇编程序而异。

有时使用单个字节更方便,因此这些寄存器允许我们单独设置其高字节和低字节:

mov ax, 0             ; ax -> 0x0000 , or in  binary  0000000000000000
mov ah, 0x56          ; ax -> 0x5600
mov al, 0x23          ; ax -> 0x5623
mov ah, 0x16          ; ax -> 0x1623
3.3.3 全部放在一起

所以,回想一下,我们希望BIOS为我们在屏幕上打印一个字符,并且我们可以通过将ax设置为某些BIOS定义的值然后触发特定的中断来调用特定的BIOS例程。 我们想要的具体例程是BIOS滚动远程类型例程,它将在屏幕上打印单个字符并前进光标,为下一个字符做好准备。 发布了一整套BIOS例程,显示了使用哪个中断以及如何在中断之前设置寄存器。 在这里,我们需要中断0x10并设置ah到0x0e(表示远程类型模式)和al中

本文标签: 第三章扇区从零开始操作系统模式