☰
交流最小内核的学习和实现方法
2026/10/1 10:27:57 网站建设 项目流程
什么是操作系统?

操作系统是一种控制计算机系统及其资源运行的软件。说到底,一个操作系统在无依托的环境运行是任务重要的工作,因为上面所有的应用都在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_ptECX-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 文本模式显存,而写的是 "字符 + 颜色属性" 对,不是普通字符串。

  1. 内存映射 I/O:0xB8000 是显卡的显存基址,显卡硬件会持续扫描这块内存并把它渲染到屏幕上。CPU 往这个地址写数据 = 直接改屏幕,不需要任何输出函数。
  2. 每 2 字节 = 1 个屏幕字符:
    • 低字节(video_memory[0])= 字符的ASCII 码('H' = 0x48)
    • 高字节(video_memory[1])=颜色属性(0x0E)
  3. 属性字节的位含义:低 4 位前景色(16 色)、中间 3 位背景色(8 色)、最高位闪烁 / 亮度。
    • 0x0E =0000 1110→ 前景1110(14 = 亮黄)、背景000(黑)→黑底黄字
  4. 所以代码里的'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

编译的运行结果

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询