欢迎光临
我们一直在努力

交流最小内核的学习和实现方法

什么是操作系统?

       操作系统是一种控制计算机系统及其资源运行的软件。说到底,一个操作系统在无依托的环境运行是任务重要的工作,因为上面所有的应用都在OS(操作系统简称),里面运行。

需要的必备知识?

        初次接触操作系统的学习应该掌握不用太多的知识,现学现用和保持兴趣比一开始的完美更重要,所有直接开始这个不太困难的学习吧。

怎样的开发环境?

        这个看你第一次接触是选择32位的、还是64位的操作系统开始写,需要提醒的是32位是一切开发操作系统的理解底层,虽然现在的64位操作系统是日常使用的主流。

GCC 交叉编译器?

        交叉编译器是需要经常接触的朋友,在现在的编译已经由Makefile来组织,可还能使用build.sh这样的Shell脚本文件来使用,算是一个专业的组织、一个临时的运行。

接下来进入正式的学习

下载需要的编程环境

Windows、macOS(苹果电脑)、Linux(开源操作系统),WSL(Linux环境的Windows子系统),安装需要工具的麻烦程度不同

这个自己找AI去安装需要的开发工具,环境多样、命令复杂、习惯不同,就不浪费篇幅了。

引导程序是什么东西

[bits 16]
[org 0x7c00]

start:
mov ax, 0
mov es, ax
mov bx, 0x1000
mov ah, 0x02
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0
int 0x13
jc disk_error

cli
lgdt [gdt_desc]

mov eax, cr0
or eax, 1
mov cr0, eax

jmp 0x08:pm_entry

[bits 32]
pm_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov esp, 0x7c00

mov edi, 0x2000
mov ecx, 1024
xor eax, eax
rep stosd

mov dword [0x2000], 0x3003

mov edi, 0x3000
mov ecx, 1024
xor eax, eax
rep stosd

mov ebx, 0x3000
mov eax, 0x00000003
mov ecx, 1024

fill_pt:
mov [ebx], eax
add ebx, 4
add eax, 0x1000
loop fill_pt

mov eax, 0x2000
mov cr3, eax

mov eax, cr0
or eax, 0x80000000
mov cr0, eax

jmp 0x08:0x1000

disk_error:
cli
hlt

gdt_start:
dq 0
gdt_code:
dw 0xffff
dw 0
db 0
db 0x9a
db 0xcf
db 0
gdt_data:
dw 0xffff
dw 0
db 0
db 0x92
db 0xcf
db 0
gdt_end:

gdt_desc:
dw gdt_end – gdt_start – 1
dd gdt_start

times 510 – ($-$$) db 0
dw 0xaa55

语法讲解(NASM 汇编,Intel 风格)

1. 方括号开头的三条是 "编译指示"(伪指令),不是 CPU 指令

  • [bits 16] / [bits 32]:告诉 NASM 生成 16 位还是 32 位机器码。代码前半段是实模式(16 位),后半段切到保护模式(32 位),所以一文件两段
  • [org 0x7c00]:声明代码的加载地址。BIOS 约定把引导扇区放到 0x7C00,NASM 依据它计算所有标号的绝对地址

2. 标号(label) start:、disk_error:、gdt_start:、pm_entry: 都是地址符号,供 jmp、loop、jc 定位。pm_entry 前面没有分隔指令,靠 jmp 0x08:pm_entry 这个远跳转直接跨过去。

3. 数据定义伪指令

  • db/dw/dd/dq:依次定义 1/2/4/8 字节数据。GDT 的三个描述符就是用手写的 8 字节拼出来的
  • times 510 – ($-$$) db 0:填充语句。$ = 当前位置,$$ = 本段起始,$-$$ = 已写入字节数,整体意思是 "把扇区填满到 510 字节"
  • dw 0xaa55:引导扇区魔数(55 AA),BIOS 靠它确认这是个可启动盘

4. GDT 手工构造 gdt_start: dq 0(空描述符)+ gdt_code + gdt_data。lgdt [gdt_desc] 把 GDTR 加载。两个描述符都是扁平模型(base=0,limit=4GB),代码段类型 0x9A、数据段 0x92。0x08 是代码段选择子(index 1,RPL=0),0x10 是数据段选择子(index 2)。

5. 关键指令

表格

指令作用
int 0x13(AH=0x02) BIOS 磁盘服务:读扇区。AL = 数量、CH/CL = 柱面 / 扇区、DH = 磁头、ES:BX = 目标地址 → 读 loader.bin 到 0x1000
jc disk_error 进位标志 CF=1(读取失败)则跳转
cli / lgdt 关中断 / 加载全局描述符表
mov eax,cr0; or eax,1; mov cr0,eax 读 – 改 – 写 CR0,置 PE 位 = 进入保护模式
rep stosd 把 EAX 反复写入 ES:EDI(每次 +4,ECX 递减到 0),用于快速清零 4KB 区域
mov dword [0x2000], 0x3003 页目录第 0 项 = 页表地址 0x3000 | 0x3(Present + 可写)
loop fill_pt ECX-1,不为 0 则跳回,循环填 1024 个页表项,每项映射一页 4KB,共 4MB(恒等映射)
mov cr3, eax / or eax, 0x80000000 载入页目录基址 / 置 CR0 的 PG 位 = 开启分页
属于什么编程风格

Intel 语法 + NASM 方言 操作数顺序是 "目的在前、源在后"(mov ds, ax),这是 Intel 语法;与 GNU 汇编的 AT&T 语法(movl %ax, %ds)正好相反。[org]、times、$/$$ 这些是 NASM 特有的写法。

要怎么能打印 "颜色字符"?

void kernel_main() {
char *video_memory = (char*) 0xb8000;
video_memory[0] = 'H';
video_memory[1] = 0x0e;
video_memory[2] = 'e';
video_memory[3] = 0x0e;
video_memory[4] = 'l';
video_memory[5] = 0x0e;
video_memory[6] = 'l';
video_memory[7] = 0x0e;
video_memory[8] = 'o';
video_memory[9] = 0x0e;
video_memory[10] = ' ';
video_memory[11] = 0x0e;
video_memory[12] = 'W';
video_memory[13] = 0x0e;
video_memory[14] = 'o';
video_memory[15] = 0x0e;
video_memory[16] = 'r';
video_memory[17] = 0x0e;
video_memory[18] = 'l';
video_memory[19] = 0x0e;
video_memory[20] = 'd';
video_memory[21] = 0x0e;
video_memory[22] = '!';
video_memory[23] = 0x0e;
while(1);
}

ENTRY(kernel_main)
SECTIONS {
. = 0x1000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}

为什么能打印 "颜色字符"?

因为 0xB8000 是 VGA 文本模式显存,而写的是 "字符 + 颜色属性" 对,不是普通字符串。

  • 内存映射 I/O:0xB8000 是显卡的显存基址,显卡硬件会持续扫描这块内存并把它渲染到屏幕上。CPU 往这个地址写数据 = 直接改屏幕,不需要任何输出函数。
  • 每 2 字节 = 1 个屏幕字符:
    • 低字节(video_memory[0])= 字符的 ASCII 码('H' = 0x48)
    • 高字节(video_memory[1])= 颜色属性(0x0E)
  • 属性字节的位含义:低 4 位前景色(16 色)、中间 3 位背景色(8 色)、最高位闪烁 / 亮度。
    • 0x0E = 0000 1110 → 前景 1110(14 = 亮黄)、背景 000(黑)→ 黑底黄字
  • 所以代码里的 'H' 和 0x0e 必须成对出现—— 前者决定显示什么字,后者决定显示成什么颜色。24 个赋值正好是 12 个字符 × 2 字节。
  • 这就是裸机编程里的 "最原始输出":没有 printf、没有系统调用,直接写显存。改一下属性字节(比如 0x1e、0x07),字色就变。

    这个链接脚本的作用是什么?

    它的作用是:告诉链接器 "代码放在哪个物理地址、段怎么排、入口在哪"—— 因为裸机内核没有操作系统来帮你加载。

    怎么编译运行?

    nasm -f bin entry.asm -o entry.bin
    gcc -m32 -ffreestanding -fno-pic -c loader.c -o loader.o
    ld -m elf_i386 -Ttext 0x1000 –oformat binary -o loader.bin loader.o
    dd if=/dev/zero of=os.img bs=512 count=100
    dd if=entry.bin of=os.img bs=512 count=1 conv=notrunc
    dd if=loader.bin of=os.img bs=512 seek=1 conv=notrunc
    qemu-system-x86_64 -drive format=raw,file=os.img

    编译的运行结果

    赞(0)
    未经允许不得转载:171主机测评 » 交流最小内核的学习和实现方法
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址