引导的汇编代码如下:
#include "asm.h" #include "memlayout.h" #include "mmu.h" # Start the first CPU: switch to 32-bit protected mode, jump into C. # The BIOS loads this code from the first sector of the hard disk into # memory at physical address 0x7c00 and starts executing in real mode # with %cs=0 %ip=7c00. .code16 # Assemble for 16-bit mode .globl start start: cli # BIOS enabled interrupts; disable # Zero data segment registers DS, ES, and SS. xorw %ax,%ax # Set %ax to zero movw %ax,%ds # -> Data Segment movw %ax,%es # -> Extra Segment movw %ax,%ss # -> Stack Segment # Physical address line A20 is tied to zero so that the first PCs # with 2 MB would run software that assumed 1 MB. Undo that. seta20.1: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.1 movb $0xd1,%al # 0xd1 -> port 0x64 outb %al,$0x64 seta20.2: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.2 movb $0xdf,%al # 0xdf -> port 0x60 outb %al,$0x60 # Switch from real to protected mode. Use a bootstrap GDT that makes # virtual addresses map directly to physical addresses so that the # effective memory map doesn't change during the transition. lgdt gdtdesc movl %cr0, %eax orl $CR0_PE, %eax movl %eax, %cr0 //PAGEBREAK! # Complete transition to 32-bit protected mode by using long jmp # to reload %cs and %eip. The segment descriptors are set up with no # translation, so that the mapping is still the identity mapping. ljmp $(SEG_KCODE<<3), $start32 .code32 # Tell assembler to generate 32-bit code now. start32: # Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax # Our data segment selector movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %ss # -> SS: Stack Segment movw $0, %ax # Zero segments not ready for use movw %ax, %fs # -> FS movw %ax, %gs # -> GS # Set up the stack pointer and call into C. movl $start, %esp call bootmain # If bootmain returns (it shouldn't), trigger a Bochs # breakpoint if running under Bochs, then loop. movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 movw %ax, %dx outw %ax, %dx movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 outw %ax, %dx spin: jmp spin # Bootstrap GDT .p2align 2 # force 4 byte alignment gdt: SEG_NULLASM # null seg SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 .long gdt # address gdt
分析如下(这分析是从网上找来的):
引导加载器(boot loader)
当 x86 PC 启动时,它执行的是一个叫 BIOS 的程序。BIOS 存放在非易失存储器中,BIOS 的作用是在启动时进行硬件的准备工作,接着把控制权交给操作系统。具体来说,BIOS 会把控制权交给从引导扇区(用于引导的磁盘的第一个512字节的数据区)加载的代码。引导扇区中包含引导加载器——负责内核加载到内存中。BIOS 会把引导扇区加载到内存 0x7c00 处,接着(通过设置寄存器 %ip
)跳转至该地址。引导加载器开始执行后,处理器处于模拟 Intel 8088 处理器的模式下。而接下来的工作就是把处理器设置为现代的操作模式,并从磁盘中把 xv6 内核载入到内存中,然后将控制权交给内核。xv6 引导加载器包括两个源文件,一个由16位和32位汇编混合编写而成(bootasm.S
;(8400)),另一个由 C 写成(bootmain.c
;(8500))。
代码:汇编引导程序
引导加载器的第一条指令 cli
(8412)屏蔽处理器中断。硬件可以通过中断触发中断处理程序,从而调用操作系统的功能。BIOS 作为一个小型操作系统,为了初始化硬件设备,可能设置了自己的中断处理程序。但是现在 BIOS 已经没有了控制权,而是引导加载器正在运行,所以现在还允许中断不合理也不安全。当 xv6 准备好了后(详见第3章),它会重新允许中断。
现在处理器处在模拟 Intel 8088 的实模式下,有8个16位通用寄存器可用,但实际上处理器发送给内存的是20位的地址。这时,多出来的4位其实是由段寄存器%cs, %ds, %es, %ss
提供的。当程序用到一个内存地址时,处理器会自动在该地址上加上某个16位段寄存器值的16倍。因此,内存引用中其实隐含地使用了段寄存器的值:取指会用到 %cs
,读写数据会用到 %ds
,读写栈会用到 %ss
。
xv6 假设 x86 指令在做内存操作时使用的是虚拟地址,但实际上 x86 指令使用的是逻辑地址(见表 B-1)。逻辑地址由段选择器和偏移组成,有时又被写作segmemt:offset。更多时候,段是隐含的,所以程序会直接使用偏移。分段硬件会完成上述处理,从而产生一个线性地址。如果允许分页硬件工作(见第2章),分页硬件则会把线性地址翻译为物理地址;否则处理器直接把线性地址看作物理地址。
引导加载器还没有允许分页硬件工作;它通过分段硬件把逻辑地址转化为线性地址,然后直接作为物理地址使用。xv6 会配置分段硬件,使之不对逻辑地址做任何改变,直接得到线性地址,所以线性地址和逻辑地址是相等的。由于历史原因我们用虚拟地址这个术语来指程序操作时用的地址。xv6 的虚拟地址等于 X86 的逻辑地址,同样也等于分段硬件映射的线性地址。等到开启了分页后,系统中值得关心的就只有从线性地址到物理地址的映射。
BIOS 完成工作后,%ds, %es, %ss
的值是未知的,所以在屏蔽中断后,引导加载器的第一个工作就是将 %ax
置零,然后把这个零值拷贝到三个段寄存器中(8415-8418)。
虚拟地址 segment:offset 可能产生21位物理地址,但 Intel 8088 只能向内存传递20位地址,所以它截断了地址的最高位:0xffff0 + 0xffff = 0x10ffef,但在8088上虚拟地址 0xffff:0xffff 则是引用物理地址 0x0ffef。早期的软件依赖硬件来忽略第21位地址位,所以当 Intel 研发出使用超过20位物理地址的处理器时,IBM 就想出了一个技巧来保证兼容性。那就是,如果键盘控制器输出端口的第2位是低位,则物理地址的第21位被清零;否则,第21位可以正常使用。引导加载器用 I/O 指令控制端口 0x64 和 0x60 上的键盘控制器,使其输出端口的第2位为高位,来使第21位地址正常工作(8436)。
对于使用内存超过65536字节的程序而言,实模式的16位寄存器和段寄存器就显得非常困窘了,显然更不可能使用超过 1M 字节的内存。x86系列处理器在80286之后就有了保护模式。保护模式下可以使用更多位的地址,并且(80386之后)有了“32位”模式使得寄存器,虚拟地址和大多数的整型运算都从16位变成了32位。xv6 引导程序依次允许了保护模式和32位模式。
在保护模式下,段寄存器保存着段描述符表的索引(见图表 B-2)。每一个表项都指定了一个基物理地址,最大虚拟地址(称为限制),以及该段的权限位。这些权限位在保护模式下起着保护作用,内核可以根据它们来保证一个程序只使用属于自己的内存。
xv6 几乎没有使用段;取而代之的是第2章讲述的分页。引导加载器将段描述符表 gdt
(8482-8485)中的每个段的基址都置零,并让所有段都有相同的内存限制(4G字节)。该表中有一个空指针表项,一个可执行代码的表项,一个数据的表项。代码段描述符的标志位中指示了代码只能在32位模式下执行(0660)。正是由于这样的设置,引导加载器在进入保护模式时,逻辑地址才会直接映射为物理地址。
引导加载器执行 lgdt
(8441)指令来把指向 gdt
的指针 gdtdesc
(8487-8489)加载到全局描述符表(GDT)寄存器中。
加载完毕后,引导加载器将 %cr0
中的 CR0_PE
位置为1,从而开启保护模式。允许保护模式并不会马上改变处理器把逻辑地址翻译成物理地址的过程;只有当处理器读取 GDT 地址的段寄存器的值被改变为内部段的设置后,才会发生变化。我们没法直接修改 %cs
,所以使用了一个 ljmp
指令(8453)。跳转指令会接着在下一行(8456)执行,但这样做实际上将 %cs
指向了 gdt
中的一个代码描述符表项。该描述符描述了一个32位代码段,这样处理器就切换到了32位模式下。就这样,引导加载器让处理器从8088进化到80286,接着进化到了80386。
在32位模式下,引导加载器首先用 SEG_KDATA
(8458-8461)初始化了数据段寄存器。逻辑地址现在是直接映射到物理地址的。运行 C 代码之前的最后一个步骤是在空闲内存中建立一个栈。内存 0xa0000 到 0x100000 属于设备区,而 xv6 内核则是放在 0x100000 处。引导加载器自己是在 0x7c00 到 0x7d00。本质上来讲,内存的其他任何部分都能用来存放栈。引导加载器选择了 0x7c00(在该文件中即 $start
)作为栈顶;栈从此处向下增长,直到 0x0000,不断远离引导加载器代码。
最后加载器调用 C 函数 bootmain
(8468)。bootmain
的工作就是加载并运行内核。只有在出错时该函数才会返回,这时它会向端口 0x8a00(8470-8476)输出几个字。在真实硬件中,并没有设备连接到该端口,所以这段代码相当于什么也没有做。如果引导加载器是在 PC 模拟器上运行,那么端口 0x8a00 则会连接到模拟器并把控制权交还给模拟器本身。无论是否使用模拟器,这段代码接下来都会执行一个死循环(8477-8478)。而一个真正的引导加载器则应该会尝试输出一些调试信息。
相关推荐
【xv6操作系统实验环境的搭建】涉及到的知识点主要包括虚拟化技术、操作系统的模拟运行以及Xv6操作系统的编译和调试。以下是这些知识点的详细解释: 1. **虚拟化技术**:虚拟化技术允许在一台物理主机上运行多个...
### 清华大学xv6实验报告:操作系统设计与实现 #### 一、引言 操作系统作为连接硬件和用户之间的桥梁,在计算机科学中扮演着极其重要的角色。它不仅需要管理和调度计算机系统的各种资源,还需要为用户提供友好的...
### MIT xv6 Unix 第六版操作系统源码讲解 #### 前言与致谢 这份文档是一份旨在为操作系统课程提供支持的教学材料草稿。它通过研究一个名为xv6的例子内核来解释操作系统的主概念。xv6是Dennis Ritchie和Ken ...
XV6操作系统是一款微型操作系统,主要用于教育目的,特别是在麻省理工学院(MIT)的操作系统课程中广泛使用。这款操作系统的设计简单明了,旨在帮助学生理解操作系统的基本原理和核心功能,如进程管理、内存分配、文件...
这本书的设计理念受到了John Lions的《Commentary on UNIX 6th Edition》的启发,旨在通过分析XV6的源代码来深入理解操作系统的核心概念。 **操作系统的接口**是书中的第一个主题。操作系统的主要任务是为多个程序...
xv6是一个由麻省理工学院(MIT)开发的教学操作系统内核,专门为了操作系统课程的教学而设计。它旨在帮助学生理解操作系统中的一些核心概念。xv6的英文原始文档已经被翻译成中文,形成了这份中文文档。该文档详细地...
这个实验系列通常包括多个阶段,从lab1到lab6,逐步引导学生探索操作系统的核心功能,如进程管理、内存管理、调度算法、设备驱动等。在这个"操作系统ucore实验lab1~lab6答案版"的压缩包中,包含了完整的实验解决方案...
综上所述,xv6中文文档为读者提供了一个深入理解操作系统基本概念的途径,通过阅读和分析xv6的代码,学习者可以获得宝贵的实践经验和理论知识,这对于操作系统的学习和研究有着不可估量的价值。
《MIT xv6与JOS操作系统详解》 操作系统是计算机科学中的核心课程,MIT的xv6和JOS是两个广泛用于教学的操作系统实现。这两个系统为学生提供了深入了解操作系统原理、内核编程以及并发控制的实践平台。下面我们将...
### xv6操作系统知识点详解 #### 一、xv6操作系统简介 xv6是一个简单而类Unix的教学操作系统,由Russ Cox、Frans Kaashoek和Robert Morris共同编写。该系统是基于Dennis Ritchie与Ken Thompson开发的Unix Version 6...
- **目标受众**:xv6主要面向对操作系统感兴趣的大学生,特别是那些参加MIT操作系统课程6.828的学生。 - **技术特性**:xv6遵循v6的设计结构和风格,但是使用ANSI C编写,并针对x86多核处理器进行了优化。 #### 二...
文档的前面部分和致谢部分提到了xv6操作系统开发过程中做出贡献的人们。特别是对MIT操作系统课程6.828中的教学人员、助教以及学生们表示感谢,他们直接或间接地为xv6做出了贡献。此外,作者也感谢那些发现并报告文本...
- **Boot xv6 (easy)**:这部分涉及操作系统的启动过程,包括引导加载器、内核初始化等,学习者需要理解如何编写启动代码使得Xv6能够正确运行。 - **sleep (easy)**:实现进程的睡眠和唤醒,涉及进程调度和上下文...
在介绍MIT操作系统Xv6之前,我们需要先了解一些操作系统的基础知识。操作系统(Operating System, OS)是管理和控制计算机硬件与软件资源的程序,它为应用软件提供了一个与硬件交互的接口,使得用户可以更加方便地...
通常,这样的文件扩展名可能是.exe,表示这是一个可执行文件,用于在Windows操作系统上启动安装过程。 从这些信息中,我们可以推测出以下知识点: 1. **安装程序**:N6XV_SETUP很可能是N6XV软件的安装包,用于在...
最初的xv6是一个简单的Unix之类的操作系统,用于MIT的教育,目标是x86。 有关更多详细信息,请参见 我们为移植所做的工作 移植不仅仅需要重写xv6。 这是我们为移植所做的。 创建工具链 创建几乎与
操作系统是计算机系统的核心组成部分,...对于初学者,可以从简单的操作系统设计如 xv6 或 MINIX 开始,逐步掌握操作系统的核心原理。同时,阅读相关的书籍、参加讨论论坛和实际项目经验都是提升操作系知识的重要途径。
第一个课题基于《Orange'S:一个操作系统的实现》和《xv6及Labs课程项目》文档,要求学生以小组形式(每组1-3人)完成,重点在于理解操作系统的基础架构,包括引导加载、内核、文件系统和控制台的实现。每个小组成员...
操作系统是计算机系统的核心组成部分,负责管理和协调计算机硬件与软件资源,提供给用户和应用程序一个抽象的接口。在“给操作系统捋条线1...如果对文中提到的Xv6操作系统感兴趣,可以通过作者提供的资源获取更多信息。