当前位置: 首页 > news >正文

从0手写自己的Linux x86操作系统(视频教程)

概述

  • 手写Linux x86操作系统是一项涉及计算机底层原理、汇编语言、C语言和操作系统内核设计的复杂工程,需分阶段逐步实现。
  • 视频教程:https://pan.quark.cn/s/ba54cd9d2ebc

一、前置知识与环境准备

在开始编写代码前,需掌握底层知识并搭建开发环境,确保能够编译、调试和运行操作系统代码。

1.1 必备前置知识

手写操作系统需要深入理解计算机底层逻辑,核心知识包括:

  • x86架构基础:实模式(16位)、保护模式(32位)/长模式(64位)的内存寻址方式、段寄存器(CS/DS/ES等)、页表机制、中断控制器(8259 PIC)、CPU特权级(Ring 0~3)。
  • 汇编语言:x86 16位汇编(用于引导程序)和32位汇编(用于内核底层操作,如中断处理、上下文切换),需熟悉NASM语法(常用的操作系统开发汇编语法)。
  • C语言底层特性:指针操作(直接访问内存地址)、结构体(描述进程、页表等数据结构)、函数指针(中断处理表、系统调用表)、无标准库编程(内核无法依赖glibc,需自己实现内存分配、字符串操作等基础函数)。
  • 操作系统核心概念:进程/线程管理(PCB、调度算法)、内存管理(分页/分段、物理内存分配)、中断与异常处理、系统调用、文件系统(inode、目录结构)、设备驱动(字符设备/块设备)。

1.2 开发环境搭建

需搭建支持x86交叉编译、镜像生成和虚拟机调试的环境,推荐使用Linux主机(Ubuntu 20.04+) ,核心工具如下:

工具名称作用安装命令(Ubuntu)
NASM汇编器,编译16位/32位x86汇编代码sudo apt install nasm
GCC x86交叉编译器编译32位内核C代码(避免主机编译器依赖64位特性)sudo apt install gcc-multilib
LD链接器,将汇编目标文件和C目标文件链接为内核镜像随GCC安装,无需额外操作
QEMU虚拟机,运行操作系统镜像并支持调试sudo apt install qemu-system-x86
GDB调试工具,配合QEMU调试内核代码sudo apt install gdb
dd生成磁盘镜像文件系统自带
parted分区工具(可选,用于复杂磁盘布局)sudo apt install parted

二、阶段1:实现引导程序(Bootloader)

计算机启动时,CPU首先执行BIOS(基本输入输出系统),BIOS会检测硬件并将硬盘第一个扇区(MBR,主引导记录,512字节)加载到内存0x7C00处,然后跳转到该地址执行。引导程序的核心任务是:将内核加载到内存,并从实模式切换到保护模式,最终跳转到内核入口

2.1 编写MBR引导程序(16位汇编)

MBR是引导的第一阶段,需实现硬件检测、读取内核到内存、切换保护模式的基础工作。以下是简化的MBR代码(mbr.asm):

org 0x7C00          ; 告诉汇编器,代码加载到0x7C00处
bits 16             ; 16位实模式start:; 初始化段寄存器(实模式下,段地址+偏移地址=物理地址)mov ax, 0x00mov ds, ax      ; 数据段寄存器=0x00mov es, ax      ; 附加段寄存器=0x00mov ss, ax      ; 栈段寄存器=0x00mov sp, 0x7C00  ; 栈指针指向0x7C00(栈向下生长,避免覆盖代码); 清屏(BIOS中断0x10,功能号0x06:滚动清屏)mov ah, 0x06mov al, 0x00    ; 滚动行数=0(清屏)mov ch, 0x00    ; 左上角行号mov cl, 0x00    ; 左上角列号mov dh, 0x18    ; 右下角行号(24行)mov dl, 0x4F    ; 右下角列号(80列)mov bh, 0x07    ; 字符属性(黑底白字)int 0x10; 显示引导信息(BIOS中断0x10,功能号0x13:显示字符串)mov ah, 0x13mov al, 0x01    ; 字符串模式:显示后光标移动mov bh, 0x00    ; 页号mov bl, 0x02    ; 字符属性(绿底黑字)mov cx, msg_len ; 字符串长度mov dh, 0x00    ; 行号mov dl, 0x00    ; 列号push axmov ax, dsmov es, ax      ; ES=DS(字符串在DS段)pop axmov bp, boot_msg ; BP=字符串偏移地址int 0x10; 读取内核到内存0x10000处(BIOS中断0x13,功能号0x02:读扇区)mov ah, 0x02    ; 功能号:读扇区mov al, 0x08    ; 读取扇区数(假设内核占8个扇区,可根据实际调整)mov ch, 0x00    ; 磁道号(0号磁道)mov cl, 0x02    ; 扇区号(从2号扇区开始,1号扇区是MBR)mov dh, 0x00    ; 磁头号(0号磁头)mov dl, 0x80    ; 驱动器号(0x80=第一块硬盘)mov bx, 0x1000  ; ES:BX = 目标地址(0x0000:0x1000 = 0x10000)mov es, bxmov bx, 0x0000int 0x13        ; 调用BIOS中断读扇区jc read_error   ; 若CF=1,读取失败,跳转报错; 准备切换到保护模式:开启A20地址线(实模式下A20被屏蔽,只能访问1MB内存)in al, 0x92     ; 读取端口0x92(系统控制端口)or al, 0x02     ; 置位第1位(开启A20)out 0x92, al    ; 写回端口; 加载全局描述符表(GDT),保护模式必备(定义段的基地址、限长、权限)lgdt [gdt_descriptor]; 切换到保护模式:将CR0寄存器的PE位(第0位)置1mov eax, cr0or eax, 0x01    ; PE=1(保护模式使能)mov cr0, eax; 远跳转到代码段(清空流水线,确保保护模式生效)jmp CODE_SEG:init_protected; 读取扇区失败处理
read_error:mov ah, 0x13mov al, 0x01mov bh, 0x00mov bl, 0x04    ; 红底黑字(错误提示)mov cx, err_lenmov dh, 0x01mov dl, 0x00push axmov ax, dsmov es, axpop axmov bp, err_msgint 0x10jmp $           ; 死循环; 全局描述符表(GDT):保护模式下,内存访问需通过段描述符
; GDT格式:8字节/描述符,包含基地址(32位)、限长(20位)、权限位
gdt_start:; 空描述符(GDT第一个描述符必须为空)gdt_null:dd 0x00000000dd 0x00000000; 代码段描述符:基地址=0x00000000,限长=0xFFFFF(4GB,页粒度),权限=Ring 0(内核级)gdt_code:dw 0xFFFF       ; 限长(低16位)dw 0x0000       ; 基地址(低16位)db 0x00         ; 基地址(中8位)db 0x9A         ; 权限位:Present=1, DPL=0, Code=1, Non-conforming=1, Readable=1db 0xCF         ; 限长(高4位)+ 粒度位:Granularity=1(4KB页), 32位模式=1db 0x00         ; 基地址(高8位); 数据段描述符:基地址=0x00000000,限长=0xFFFFF,权限=Ring 0gdt_data:dw 0xFFFF       ; 限长(低16位)dw 0x0000       ; 基地址(低16位)db 0x00         ; 基地址(中8位)db 0x92         ; 权限位:Present=1, DPL=0, Data=1, Expand-up=1, Writable=1db 0xCF         ; 限长(高4位)+ 粒度位db 0x00         ; 基地址(高8位)
gdt_end:; GDT描述符:告诉CPU GDT的基地址和长度(长度=GDT结束地址-GDT开始地址-1)
gdt_descriptor:dw gdt_end - gdt_start - 1  ; GDT长度(16位)dd gdt_start                ; GDT基地址(32位); 定义段选择子(GDT中描述符的索引,左移3位+权限位)
CODE_SEG equ gdt_code - gdt_start  ; 代码段选择子(索引=1,DPL=0)
DATA_SEG equ gdt_data - gdt_start  ; 数据段选择子(索引=2,DPL=0); 字符串定义
boot_msg db "Booting My Linux OS..."
msg_len equ $ - boot_msg
err_msg db "Error: Failed to load kernel!"
err_len equ $ - err_msg; 填充MBR到512字节,末尾添加启动标志0xAA55(BIOS识别MBR的标志)
times 510 - ($ - $$) db 0x00
dw 0xAA55

2.2 编译与验证MBR

  1. 编译MBR:使用NASM将汇编代码编译为二进制文件:
    nasm -f bin mbr.asm -o mbr.bin
    
  2. 生成磁盘镜像:创建一个10MB的空镜像,将MBR写入镜像的第一个扇区:
    dd if=/dev/zero of=myos.img bs=1M count=10  # 创建10MB空镜像
    dd if=mbr.bin of=myos.img bs=512 count=1 conv=notrunc  # 写入MBR(不截断镜像)
    
  3. 运行MBR:使用QEMU启动镜像,验证引导信息是否正常显示:
    qemu-system-i386 -drive format=raw,file=myos.img
    
    若正常,QEMU窗口会显示“Booting My Linux OS…”;若读取失败(如内核未写入),会显示错误信息。

2.3 实现第二阶段引导(可选,加载内核)

MBR仅512字节,无法容纳复杂逻辑(如读取大内核、解析ELF格式),因此通常需要第二阶段引导程序(Loader)

  1. 在MBR中添加代码,将Loader从硬盘扇区(如2~9扇区)加载到内存0x9000处。
  2. Loader使用32位汇编(保护模式),实现ELF格式解析(Linux内核通常是ELF文件),将内核的代码段、数据段加载到指定内存地址(如代码段到0xC0000000,内核虚拟地址起始)。
  3. 最终跳转到内核入口地址(ELF文件头中定义的e_entry)。

三、阶段2:实现内核核心模块(32位C+汇编)

内核是操作系统的核心,需实现内存管理、中断处理、进程调度等基础功能。以下从内核入口开始,逐步实现关键模块。

3.1 内核入口:从引导程序跳转到内核

引导程序(Loader)完成保护模式切换和ELF解析后,会跳转到内核入口函数(通常是kernel_main)。内核入口需先初始化硬件(如中断控制器)、设置页表,再进入主循环。

3.1.1 内核入口汇编(kernel_entry.asm

用于初始化栈和段寄存器,调用C语言写的kernel_main

bits 32             ; 32位保护模式
global kernel_entry ; 导出入口函数,供Loader调用
extern kernel_main  ; 声明C语言实现的kernel_main; 内核栈:在内存0x80000~0x90000处分配16KB栈空间
KERNEL_STACK equ 0x90000kernel_entry:; 初始化数据段寄存器(使用GDT中的数据段选择子)mov ax, DATA_SEGmov ds, axmov es, axmov fs, axmov gs, axmov ss, ax      ; 栈段使用数据段; 初始化栈指针mov esp, KERNEL_STACK; 调用C语言内核主函数call kernel_main; 内核返回后死循环(防止CPU跑飞)jmp $
3.1.2 内核主函数(kernel.c

内核初始化的入口,先实现简单的屏幕输出(VGA文本模式),再逐步添加其他模块:

#include "vga.h"  // 自定义VGA输出头文件// 内核主函数
void kernel_main() {// 初始化VGA文本模式(清屏,设置光标位置)vga_init();// 在屏幕上显示内核启动信息vga_print("Welcome to My Linux OS Kernel!\n");vga_print("Kernel is running in 32-bit Protected Mode.\n");// 内核主循环(后续添加进程调度、中断处理等)while (1) {// 暂时空循环,后续替换为调度逻辑}
}

3.2 实现VGA文本模式输出(内核调试必备)

内核无法依赖BIOS中断(保护模式下BIOS不可用),需直接操作VGA硬件寄存器实现屏幕输出。VGA文本模式的显存地址为0xB8000(物理地址),每个字符占2字节:1字节ASCII码 + 1字节属性(前景色+背景色)。

3.2.1 VGA工具函数(vga.h + vga.c
// vga.h
#ifndef VGA_H
#define VGA_H#include <stdint.h>
#include <stddef.h>// VGA文本模式属性:高4位背景色,低4位前景色(0=黑,1=蓝,2=绿,3=青,4=红,5=紫,6=棕,7=灰)
#define VGA_COLOR_BLACK 0x0
#define VGA_COLOR_BLUE 0x1
#define VGA_COLOR_GREEN 0x2
#define VGA_COLOR_CYAN 0x3
#define VGA_COLOR_RED 0x4
#define VGA_COLOR_MAGENTA 0x5
#define VGA_COLOR_BROWN 0x6
#define VGA_COLOR_LIGHT_GREY 0x7// 组合前景色和背景色(默认黑底白字)
#define VGA_ENTRY_COLOR(fg, bg) ((bg << 4) | fg)
#define VGA_DEFAULT_COLOR VGA_ENTRY_COLOR(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)// 组合字符和属性
#define VGA_ENTRY(uc, color) ((uint16_t)(uc) | (uint16_t)(color) << 
http://www.dtcms.com/a/419716.html

相关文章:

  • 有模板怎么建站南通市住房建设局网站
  • 高阶常系数线性微分方程求解方法全解析
  • Visual Studio2022 opencv4.12编译viz功能注意
  • 欧洲网站服务器手机上有那种网站吗
  • 网站 国外服务器网络广告和传统广告的区别
  • 4.1 网络层的功能 (答案见原书 P134)
  • 网站备案核验单清晰申请网站空间是申请域名吗
  • 网站建设图文漯河专业做网站的公司
  • windows显示驱动开发-间接显示驱动程序
  • mybatis物理删除某条记录
  • 盘锦威旺做网站建设公司wordpress插件 stock
  • Seedream 4.0阅读总结中文翻译
  • 广州公共资源建设工程交易中心网站app开发培训班
  • 华硕NUC 15Pro 系列 舒适办公新体验的理想之选
  • 企业网站建设中在方案设计上网站建设 建议
  • 智能合约的更新与迭代
  • C语言实战项目:贪吃蛇(2)
  • 南头专业外贸网站建设公司中国建设银行官网首页登录入口
  • 如何做微信网站防封网站建设 用ftp上传文件
  • C++ STL学习笔记: Vector
  • CSS中 min() max() clamp()函数
  • 如何做免费企业网站小程序在建网站吗
  • sourcefare从入门到实战(2) - 创建第一个扫描项目(服务端Git方式)
  • 用html做网站源代码龙岩北京网站建设
  • Qt常用控件之QComboBox
  • 钢铁舞者:当机械臂成为机器人的“双手”,世界正被重塑
  • 从云端到终端,从大模型到机器人:智源众智FlagOS 1.5引领开放计算生态迈向成熟
  • 舆情网站直接打开的软件第三方商城网站建设
  • 网站seo收录工具北京建设银行纪念钞预定官方网站
  • 盐山县招聘网站建设上海建设工程安全监理网站