Linux内核系列—操作系统开发之保护模式的优势

在上一篇中我们虽然成功进入了保护模式,但是并没有体验到保护模式带给我们的便利。其实在保护模式下寻址空间可以达到4GB,实模式下1MB的寻址能力差得太远了。那么下面,我们就把程序稍作修改,体验一下它对超过1MB内存的访问能力。

我们来试验一下读写大地址内存。在前面程序的基础上,新建一个段,这个段以5MB为基址,远远超出实模式下1MB的界限。我们先读出开始处8字节的内容,然后写入一个字符串,再从中读出8字节,如下所示:

	call	TestRead
	call	TestWrite
	call	TestRead

如果读写成功的话,两次读出的内容应该是不同的,而且第二次读出的内容应该是我们写进的字符串。字符串是保存在数据段中的,也是新增加的。增加的两个段如下所示:

LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    ; Data
LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW

SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorTest		equ	LABEL_DESC_TEST		- LABEL_GDT

[SECTION .data1]	 ; 数据段
ALIGN	32
[BITS	32]
LABEL_DATA:
BootMessage:		db	"Joey, I‘m in protected mode!"
OffsetPMMessage		equ	BootMessage - $$		;表示字符串BootMessage相对于本节的开始处(LABEL_DATA)的偏移
StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest		equ	StrTest - $$
DataLen			equ	$ - LABEL_DATA
; END of [SECTION .data1]

段[SECTION .s32]这个段的开头初始化了ds、es和gs,让ds指向新增的数据段,es指向新增的5MB内存的段,gs指向显存。如下所示:

LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	ax, SelectorTest
	mov	es, ax			; 测试段选择子
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

数据段的基址便是LABEL_DATA的物理地址。于是OffsetStrTest既是字符串相对于LABEL_DATA的偏移,也是其在数据段中的偏移。我们在保护模式下需要用到的正是这个偏移,而不再是实模式下的地址。section的一点妙用指的便是这里的$$。

	; 初始化数据段描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_DATA
	mov	word [LABEL_DESC_DATA + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_DATA + 4], al
	mov	byte [LABEL_DESC_DATA + 7], ah

运行如下所示:

第一次执行call TestRead时显示8个空字符,第二次执行call TestRead时显示ABCDEFGH。因为TestRead的段寄存器指向的是5MB内存地址处的内容,一开始是没有任何数据。

TestRead:
	xor	esi, esi
	mov	ecx, 8
.loop:
	mov	al, [es:esi]
	call	DispAL
	inc	esi
	loop	.loop

	ret

然后第190行向这个5MB内存地址处依次写入8字节数据。

源码及软盘映像

时间: 2024-10-08 13:22:52

Linux内核系列—操作系统开发之保护模式的优势的相关文章

Linux内核系列—操作系统开发之进入32位保护模式

源码如下: ; ========================================== ; pmtest1.asm ; 编译方法:nasm pmtest1.asm -o pmtest1.bin ; ========================================== %include "pm.inc" ; 常量, 宏, 以及一些说明 org 07c00h jmp LABEL_BEGIN [SECTION .gdt] ; GDT ; 段基址, 段界限 , 属

Linux内核系列—12.b.操作系统开发之从Loader跳入保护模式

现在,内核已经被我们加载进内存了,该是跳入保护模式的时候了. 首先是GDT以及对应的选择子,我们只定义三个描述符,分别是一个0~4GB的可执行段.一个0~4GB的可读写段和一个指向显存开始地址的段: ; GDT ; 段基址 段界限, 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR|DA_32|DA_LIMIT_4K ;0-4G LABEL_DESC_FLAT_RW: D

Linux内核系列—12.a.操作系统开发之从Loader到内核

Loader要做两项工作,我们先来做第一项,把内核加载到内存: 1.加载内核到内存. 2.跳入保护模式. 首先编译无内核时: nasm boot.asm -o boot.bin nasm loader.asm -o loader.bin dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc sudo mount -o loop a.img /mnt/hgfs/ sudo cp loader.bin /mnt/hgfs/ -v sudo umoun

Linux内核系列—8.操作系统开发之时钟中断

外部中断的情况复杂一些,因为需要建立硬件中断与向量号之间的对应关系.外部中断分为不可屏蔽中断(NMI)和可屏蔽中断两种,分别由CPU的两根引脚NMI和INTR来接收.如下图所示: 可屏蔽中断与CPU的关系是通过对可编程中断控制器8259A建立起来的.8259A可以认为它是中断机制中所有外围设备的一个代理.在BIOS初始化它的时候,IRQ0~IRQ7被设置为对应向量号08h~0Fh,在保护模式下向量号08h~0Fh已经被占用了,所以我们不得不重新设置主从8259A. 对8259A的设置并不复杂,通

linux内核系列(一)编译安装Linux内核 2.6.18

1.配置环境 操作系统:CentOS 5.2 下载linux-2.6.18版本的内核,网址:http://www.kernel.org 说明:该编译文档适合2.6.18以上的Linux内核版本,只需所编译的 Linux内核版本不能低于Linux操作系统自身的内核版本,不然会遇到很多问题:   2.开始编译 cp  ./ linux-2.6.18.tar.gz  /usr/src/ tar –zxvf ./linux-2.6.18.tar.gz cd /usr/src/linux-2.6.18 /

linux内核系列(一)内核数据结构之链表

双向链表 传统链表与linu内核链表的区别图: 图一 图二 从上图中看出在传统链表中各种不同链表间没有通用性,因为各个数据域不同,而在linux内核中巧妙将链表结构内嵌到数据域结构中使得不同结构之间能连接起来: 链表的常用操作 内核中链表实现文件路径:include/linux/list.h 链表结构定义 struct list_head {     struct list_head *next, *prev; }; 获取结构入口地址(list_entry) #define list_entry

Linux内核系列—10.操作系统开发之内核HelloWorld

a.我们先来体验一下在Linux下用汇编编程的感觉,见代码 [section .data] ; 数据在此 strHello db "Hello, world!", 0Ah STRLEN equ $ - strHello [section .text] ; 代码在此 global _start ; 我们必须导出 _start 这个入口,以便让链接器识别 _start: mov edx, STRLEN mov ecx, strHello mov ebx, 1 mov eax, 4 ; sy

Linux内核系列—12.d.操作系统开发之扩充内核 ●

现在把esp.GDT等内容放进内核中,我们现在可以用C语言了,只要能用C,我们就避免用汇编. 下面看切换堆栈和GDT的关键代码: ; 导入函数 extern cstart ; 导入全局变量 extern gdt_ptr [SECTION .bss] StackSpace resb 2 * 1024 StackTop: ; 栈顶 ; 把 esp 从 LOADER 挪到 KERNEL mov esp, StackTop ; 堆栈在 bss 段中 sgdt [gdt_ptr] ; cstart() 中

Linux内核系列—11.操作系统开发之ELF格式

ELF文件的结构如下图所示: ELF文件由4部分组成,分别是ELF头(ELF header).程序头表(Program header table).节(Sections)和节头表(Section header table). 实际上,一个文件中不一定包含全部这些内容,而且它们的位置也未必如上图所示这样安排,只有ELF头的位置是固定的,其余各部分的位置.大小等信息由ELF头中的各项值来决定. ELF header的格式如下代码所示: #define EI_NIDENT 16 typedef str