
什么是操作系统操作系统是一种控制计算机系统及其资源运行的软件。说到底一个操作系统在无依托的环境运行是任务重要的工作因为上面所有的应用都在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 约定把引导扇区放到 0x7C00NASM 依据它计算所有标号的绝对地址2. 标号labelstart:、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 AABIOS 靠它确认这是个可启动盘4. GDT 手工构造gdt_start: dq 0空描述符gdt_codegdt_data。lgdt [gdt_desc]把 GDTR 加载。两个描述符都是扁平模型base0limit4GB代码段类型 0x9A、数据段 0x92。0x08是代码段选择子index 1RPL00x10是数据段选择子index 2。5. 关键指令表格指令作用int 0x13AH0x02BIOS 磁盘服务读扇区。AL 数量、CH/CL 柱面 / 扇区、DH 磁头、ES:BX 目标地址 → 读 loader.bin 到 0x1000jc disk_error进位标志 CF1读取失败则跳转cli/lgdt关中断 / 加载全局描述符表mov eax,cr0; or eax,1; mov cr0,eax读 - 改 - 写 CR0置 PE 位 进入保护模式rep stosd把 EAX 反复写入 ES:EDI每次 4ECX 递减到 0用于快速清零 4KB 区域mov dword [0x2000], 0x3003页目录第 0 项 页表地址 0x3000 | 0x3Present 可写loop fill_ptECX-1不为 0 则跳回循环填 1024 个页表项每项映射一页 4KB共 4MB恒等映射mov cr3, eax/or eax, 0x80000000载入页目录基址 / 置 CR0 的 PG 位 开启分页属于什么编程风格Intel 语法 NASM 方言操作数顺序是 目的在前、源在后mov ds, ax这是 Intel 语法与 GNU 汇编的 ATT 语法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/O0xB8000 是显卡的显存基址显卡硬件会持续扫描这块内存并把它渲染到屏幕上。CPU 往这个地址写数据 直接改屏幕不需要任何输出函数。每 2 字节 1 个屏幕字符低字节video_memory[0] 字符的ASCII 码H 0x48高字节video_memory[1]颜色属性0x0E属性字节的位含义低 4 位前景色16 色、中间 3 位背景色8 色、最高位闪烁 / 亮度。0x0E 0000 1110→ 前景111014 亮黄、背景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 ofos.img bs512 count100 dd ifentry.bin ofos.img bs512 count1 convnotrunc dd ifloader.bin ofos.img bs512 seek1 convnotrunc qemu-system-x86_64 -drive formatraw,fileos.img编译的运行结果