子牙-手写OS操作系统【3期】

25 阅读2分钟

0d1c2f84f8b64d66af4e171619014ed0~tplv-obj.jpg 手写操作系统:从零开始构建内核的实践之路

构建操作系统不仅仅是编写代码,更是对计算机底层运行机制的深度探索。通过亲手实现每一个核心模块,我们能够真正理解软硬件之间的交互方式。

一、 引导程序的启动与模式切换

操作系统的第一步是从 BIOS 将控制权移交给我们的代码。我们需要编写汇编引导程序,完成实模式到保护模式的关键切换,为后续的内核运行搭建好最基础的硬件环境。

; 初始化 GDT 全局描述符表
lgdt [gdt_ptr]

; 开启 A20 地址线,以便访问 1MB 以上的内存
in al, 0x92
or al, 00000010b
out 0x92, al

; 设置 CR0 寄存器的 PE 位,进入保护模式
mov eax, cr0
or eax, 1
mov cr0, eax

; 通过远跳转刷新流水线,进入 32 位保护模式
jmp dword Selector_Code32:ProtectedModeEntry

二、 内存管理机制的实现

内存管理是内核的基石。我们需要设计物理内存管理器和虚拟内存管理器,实现页表的映射,将线性地址转换为物理地址,确保每个进程都有独立的内存空间。

// 页目录项与页表项的数据结构定义
typedef struct {
    unsigned int present    : 1;    // 存在位
    unsigned int rw         : 1;    // 读写位
    unsigned int user       : 1;    // 用户/ supervisor 位
    unsigned int pwt        : 1;    // 页级直写
    unsigned int pcd        : 1;    // 页级缓存禁用
    unsigned int accessed   : 1;    // 访问位
    unsigned int dirty      : 1;    // 脏位
    unsigned int pat        : 1;    // 页属性表
    unsigned int global     : 1;    // 全局位
    unsigned int available  : 3;    // 可用位
    unsigned int frame      : 20;   // 帧地址
} page_entry_t;

// 映射虚拟地址到物理地址的核心函数
void map_page(void *phys_addr, void *virt_addr, unsigned int flags) {
    unsigned int page_index = (unsigned int)virt_addr / 0x1000;
    unsigned int table_index = page_index / 1024;
    unsigned int entry_index = page_index % 1024;

    // 获取或创建页表
    unsigned int *table = get_or_create_page_table(table_index);
    
    // 设置页表项
    table[entry_index] = ((unsigned int)phys_addr) | flags;
}

三、 进程调度与中断处理

多任务环境的实现依赖于高效的进程调度和中断处理机制。通过时钟中断触发调度器,在不同的进程上下文之间进行切换,实现看似并行运行的假象。

// 进程控制块 (PCB) 结构
struct task_struct {
    volatile unsigned int state;    // 进程状态
    unsigned long pid;              // 进程标识符
    struct pt_regs *regs;          // 寄存器保存区域
    struct task_struct *next;       // 链表指针
    unsigned long kernel_stack;    // 内核栈顶
};

// 简单的调度器函数
void schedule() {
    struct task_struct *prev = current;
    struct task_struct *next = get_next_task();

    if (prev == next) return;

    // 切换进程上下文
    switch_to(prev, next);
}

// 时钟中断处理程序
void timer_handler(struct pt_regs *regs) {
    // 更新系统时间
    jiffies++;

    // 当前进程时间片递减
    if (current->time_slice > 0) {
        current->time_slice--;
    }

    // 触发调度
    schedule();
}