引导、特权级与用户态/内核态切换模拟

本节目标

特权级(Ring):CPU 分特权等级,内核跑在最高特权(Ring 0),用户程序在最低(Ring 3)。用户想用硬件(磁盘/网络)必须经"系统调用"陷入内核。

// 运行环境:Node.js 14+
// 保存为 aj-l11.js,执行:node aj-l11.js
class CPU {
  constructor() {
    this.ring = 0          // 当前特权级:0=内核 3=用户
    this.running = null
  }
  boot() {
    this.ring = 0
    console.log('[boot] 加电,CPU 进入内核态(Ring 0)')
    this.kernelInit()
  }
  kernelInit() {
    console.log('[kernel] 初始化硬件、建立页表、加载init进程')
    this.ring = 3 // 交权给用户态
    console.log('[kernel] 切换到用户态(Ring 3),启动第一个用户程序')
  }
  // 系统调用:用户态主动"陷入"内核
  syscall(nr, arg) {
    if (this.ring !== 3) throw new Error('只有用户态能发起 syscall')
    console.log(`[user] 请求 syscall #${nr} (${arg})`)
    this.ring = 0                  // 陷入内核
    const result = this._handle(nr, arg)
    this.ring = 3                  // 返回用户态
    return result
  }
  _handle(nr, arg) {
    console.log(`[kernel] 在内核态执行 #${nr}`)
    return 'done:' + arg
  }
}

// 调用示例
const cpu = new CPU()
cpu.boot()                         // 内核初始化并交权
console.log(cpu.syscall(1, 'read disk')) // 用户程序借 syscall 用硬件

为什么不能直接碰硬件:若用户程序能随意写磁盘、改页表,一个 bug 或恶意程序就能搞垮整台机器。特权级把"危险操作"锁在内核里,用户只能通过受控入口(syscall)申请。

名词解释

课后练习

  1. 为什么用户程序不能直接读写磁盘,而要走 syscall?
    • 答案:特权隔离防止单个程序破坏系统/其他程序;内核统一仲裁,保证安全与公平。直接放行则一个错误写操作能毁掉整个文件系统。
  2. syscall 里为什么要 "ring=0 → 做事 → ring=3" 来回切?
    • 答案:进入内核必须提升特权才能执行硬件操作(ring=0),做完立刻降回用户态(ring=3),确保内核代码只在被请求时以高特权运行,平时用户程序仍受限。

总结

模拟"引导到特权切换",是把操作系统从教科书概念变成脑内动画的关键。你会发现,开机那一瞬间 CPU 从内核态起步、初始化完立刻"交权"给用户态,这个动作定义了整个系统的安全边界:内核是唯一的"特权公民",用户程序想碰硬件只能敲门(syscall)。我特别想强调这套设计的哲学意义——它不是为了麻烦你,而是用"最少信任"原则保护所有人。一个能随意改页表、写磁盘的用户程序,等于把整台机器的生杀大权交给任意代码,灾难可想而知。理解 Ring 0/3 与 syscall 的来回切换,你看 strace 打印的海量系统调用、看"用户态崩溃不影响系统"、看容器隔离,都有了统一的解释框架。操作系统不再是黑盒,而是一套你已亲手模拟过的"权限与委托"机制。