目录
[二、整体架构总览:模拟操作系统的 "四层组件"](#二、整体架构总览:模拟操作系统的 “四层组件”)
三、基础数据定义:枚举类(ProcessState与InterruptType)
[3.1 进程状态枚举(ProcessState)](#3.1 进程状态枚举(ProcessState))
[3.2 中断类型枚举(InterruptType)](#3.2 中断类型枚举(InterruptType))
[四、进程实体模拟:Process类(对应 OS 的 PCB)](#四、进程实体模拟:Process类(对应 OS 的 PCB))
[4.1 进程属性:模拟 PCB 的核心字段](#4.1 进程属性:模拟 PCB 的核心字段)
[4.2 构造方法:初始化进程(OS 创建进程的过程)](#4.2 构造方法:初始化进程(OS 创建进程的过程))
[对应 OS 理论:](#对应 OS 理论:)
[4.3 内部类RunResult:封装进程运行结果(替代 Java 的 "元组")](#4.3 内部类RunResult:封装进程运行结果(替代 Java 的 “元组”))
[4.4 核心方法run():进程的执行逻辑(模拟 CPU 运行进程)](#4.4 核心方法run():进程的执行逻辑(模拟 CPU 运行进程))
[逐步解析逻辑(结合 OS 状态转换):](#逐步解析逻辑(结合 OS 状态转换):)
[4.5 Getter/Setter:供 OS 访问进程属性](#4.5 Getter/Setter:供 OS 访问进程属性)
[五、操作系统内核模拟:OperatingSystem类(OS 的 "大脑")](#五、操作系统内核模拟:OperatingSystem类(OS 的 “大脑”))
[5.1 内核属性:模拟 OS 的核心数据结构](#5.1 内核属性:模拟 OS 的核心数据结构)
[5.2 构造方法:初始化 OS 内核](#5.2 构造方法:初始化 OS 内核)
[对应 OS 启动流程:](#对应 OS 启动流程:)
[5.3 进程创建:createProcess方法(OS 的进程创建功能)](#5.3 进程创建:createProcess方法(OS 的进程创建功能))
[对应 OS 操作:](#对应 OS 操作:)
[5.4 CPU 调度:schedule方法(时间片轮转算法)](#5.4 CPU 调度:schedule方法(时间片轮转算法))
[时间片轮转调度的详细逻辑(结合 OS 理论):](#时间片轮转调度的详细逻辑(结合 OS 理论):)
[5.5 中断处理:handleInterrupt方法(OS 的中断响应)](#5.5 中断处理:handleInterrupt方法(OS 的中断响应))
[5.6 系统调用:syscall方法(用户态→内核态切换)](#5.6 系统调用:syscall方法(用户态→内核态切换))
[5.7 OS 主循环:run方法(OS 的持续运行)](#5.7 OS 主循环:run方法(OS 的持续运行))
[八、关键 Java 语法与 OS 理论的结合点](#八、关键 Java 语法与 OS 理论的结合点)
一、引言
操作系统的核心机制:进程管理(状态转换)、CPU 调度(时间片轮转)、中断处理(响应突发事件)、系统调用(用户与内核交互)。其详细讲解可以参考【操作系统】计算机系统概述-CSDN博客。下面分 4 个核心模块,从 "是什么→为什么这么设计→具体怎么实现" 的逻辑详细讲解。
二、整体架构总览:模拟操作系统的 "四层组件"
这段代码通过 4 个核心部分,完整复现了操作系统的 "进程 - 调度 - 中断 - 用户交互" 流程,各组件对应关系如下:
| 代码组件 | 对应真实操作系统模块 | 核心作用 |
|---|---|---|
ProcessState/InterruptType |
基础数据定义层 | 定义进程状态、中断类型(避免魔法值,保证一致性) |
Process类 |
进程实体层(PCB) | 模拟进程的属性(PID、状态、运行时间)和行为(运行、阻塞) |
OperatingSystem类 |
内核核心层 | 实现进程调度、中断处理、系统调用(OS 的 "大脑") |
Main类 |
用户交互层 | 模拟用户使用 OS 的场景(创建进程、发起请求) |
先从最基础的 "数据定义层" 开始,再逐步深入核心逻辑。
三、基础数据定义:枚举类(ProcessState与InterruptType)
真实操作系统中,"进程状态" 和 "中断类型" 是固定的离散值(如进程只能是就绪 / 运行 / 阻塞 / 终止,中断只能是 I/O 完成 / 时间片用完等),Java 中用枚举(enum) 最适合定义这类 "有限且固定" 的值,避免用字符串或数字导致的混乱。
3.1 进程状态枚举(ProcessState)
java
enum ProcessState {
READY("就绪"), // 进程已准备好,等待CPU调度
RUNNING("运行"), // 进程正在占用CPU执行
BLOCKED("阻塞"), // 进程等待资源(如I/O),无法被调度
TERMINATED("终止");// 进程执行完成
private final String description; // 状态的中文描述(用于打印日志)
// 枚举的构造方法(默认private,只能在枚举内部调用)
ProcessState(String description) {
this.description = description;
}
// 重写toString:打印时显示中文描述(默认显示枚举名,如READY)
@Override
public String toString() {
return description;
}
}
关键设计点解析:
- 为什么需要
description属性? 枚举本身的名称(如READY)是英文,description存储中文描述,方便日志输出(比如打印 "进程状态:就绪" 而非 "进程状态:READY",更易读)。 - 枚举构造方法的访问修饰符? Java 枚举的构造方法默认是
private,只能在枚举内部初始化值(如READY("就绪")),外部无法创建枚举实例,保证状态的唯一性。 - 对应 OS 理论:完全遵循进程的 "五态模型"(简化为四态:就绪、运行、阻塞、终止),是进程状态转换的基础。
3.2 中断类型枚举(InterruptType)
java
enum InterruptType {
IO_COMPLETE("I/O完成中断"), // 外部设备I/O完成(如硬盘读写结束)
TIME_OUT("时间片用完"), // 时钟中断(进程占用CPU的时间片耗尽)
DIVIDE_ZERO("除零异常"), // 内部异常(程序执行错误,如除以0)
SYSTEM_CALL("系统调用"); // 自陷(用户程序请求内核服务)
private final String description;
InterruptType(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}
关键设计点解析:
- 中断类型的分类逻辑 :分为 "外部中断"(
IO_COMPLETE、TIME_OUT,来自 CPU 外部设备)和 "内部异常"(DIVIDE_ZERO、SYSTEM_CALL,来自 CPU 执行内部),对应真实 OS 的中断分类。 SYSTEM_CALL的特殊作用 :系统调用本质是 "自愿中断"(用户程序主动请求内核),所以归为中断类型,后续在syscall方法中会触发这种 "中断"(模拟用户态→内核态切换)。
四、进程实体模拟:Process类(对应 OS 的 PCB)
真实操作系统中,进程控制块(PCB) 是描述进程的核心数据结构,包含进程的所有属性(PID、状态、资源需求等)。Process类就是用 Java 对象模拟 PCB,同时实现进程的核心行为(运行、阻塞)。
4.1 进程属性:模拟 PCB 的核心字段
java
public class Process {
private int pid; // 进程唯一标识(PID):OS分配的唯一编号,避免进程混淆
private String name; // 进程名称:如"文本编辑器",方便用户识别
private int totalTime; // 进程总运行时间:进程需要执行的总时间(模拟CPU耗时)
private int remainingTime; // 进程剩余运行时间:随进程执行减少,为0时进程终止
private ProcessState state; // 进程当前状态:取值为ProcessState枚举(就绪/运行/阻塞/终止)
private boolean needIo; // 是否需要I/O操作:区分I/O密集型(如浏览器)和计算密集型(如编译器)
private int ioRemaining; // 剩余I/O时间:需要I/O时,需等待的时间(默认2个单位)
属性设计的合理性:
pid的唯一性 :后续由OperatingSystem的pidCounter递增生成,保证每个进程 PID 唯一。remainingTime的核心作用 :是判断进程是否执行完成的依据(remainingTime <= 0则终止)。needIo与ioRemaining:模拟真实进程的 I/O 需求(如浏览器加载网页需要 I/O),I/O 时进程会从 "运行" 转为 "阻塞",等待 I/O 完成后再唤醒。
4.2 构造方法:初始化进程(OS 创建进程的过程)
java
// 构造方法:创建进程时初始化所有属性
public Process(int pid, String name, int totalTime, boolean needIo) {
this.pid = pid; // 传入OS分配的唯一PID
this.name = name; // 传入进程名称
this.totalTime = totalTime; // 传入总运行时间
this.remainingTime = totalTime; // 初始剩余时间 = 总时间(进程还未执行)
this.state = ProcessState.READY; // 进程创建后默认"就绪"状态(等待CPU调度)
this.needIo = needIo; // 传入是否需要I/O
this.ioRemaining = needIo ? 2 : 0; // 需要I/O则剩余I/O时间为2,否则为0
}
对应 OS 理论:
进程创建后不会直接运行,而是进入 "就绪队列" 等待 CPU 调度,所以初始状态设为READY,这符合真实 OS 的进程创建流程(如用户双击 "文本编辑器",OS 创建进程后放入就绪队列)。
4.3 内部类RunResult:封装进程运行结果(替代 Java 的 "元组")
java
// 内部类:封装run方法的返回结果(是否终止 + 触发的中断类型)
public static class RunResult {
private boolean isTerminated; // 进程是否执行完成(true=终止,false=未终止)
private InterruptType interruptType;// 进程未终止时,触发的中断类型(如时间片用完、I/O中断)
// 构造方法:初始化返回结果
public RunResult(boolean isTerminated, InterruptType interruptType) {
this.isTerminated = isTerminated;
this.interruptType = interruptType;
}
// Getter方法:外部类(如OperatingSystem)需要获取结果
public boolean isTerminated() { return isTerminated; }
public InterruptType getInterruptType() { return interruptType; }
}
为什么需要这个内部类?
Java 没有 Python 的 "元组"(可同时返回多个不同类型的值),而run方法需要返回两个关键信息:
- 进程是否执行完成(
isTerminated); - 若未完成,触发了哪种中断(
interruptType,用于后续状态处理)。用静态内部类 封装这两个值,既保证数据关联性,又方便外部类(如OperatingSystem)获取结果。
4.4 核心方法run():进程的执行逻辑(模拟 CPU 运行进程)
run方法是Process类的核心,模拟进程在 CPU 上的执行过程,以及执行中可能的状态转换(运行→阻塞、运行→就绪、运行→终止)。
java
public RunResult run(int timeSlice) throws InterruptedException {
// 1. 进程开始运行:将状态设为"运行"
this.state = ProcessState.RUNNING;
System.out.printf("[进程运行] PID:%d %s 开始运行,剩余时间:%d%n",
pid, name, remainingTime);
// 2. 模拟CPU执行:按时间片运行(取"时间片"和"剩余时间"的最小值,避免过度执行)
int runTime = Math.min(timeSlice, remainingTime); // 实际运行时间
Thread.sleep(runTime * 100); // 用线程休眠模拟CPU执行(100ms/单位时间,避免等待过久)
this.remainingTime -= runTime; // 执行后,剩余时间减少
// 3. 检查是否需要I/O:若需要且剩余运行时间小于总时间的一半(模拟"执行到中途需I/O")
if (needIo && remainingTime < totalTime / 2) {
if (ioRemaining > 0) { // 还有未完成的I/O
this.state = ProcessState.BLOCKED; // 状态转为"阻塞"
ioRemaining--; // 剩余I/O时间减少1
System.out.printf("[进程阻塞] PID:%d 等待I/O,剩余I/O时间:%d%n",
pid, ioRemaining);
// 返回:未终止,触发I/O完成中断(后续需I/O完成后唤醒)
return new RunResult(false, InterruptType.IO_COMPLETE);
}
}
// 4. 检查是否运行完成:剩余时间<=0
if (remainingTime <= 0) {
this.state = ProcessState.TERMINATED; // 状态转为"终止"
System.out.printf("[进程终止] PID:%d %s 执行完成%n", pid, name);
return new RunResult(true, null); // 返回:已终止,无中断
}
// 5. 时间片用完:未执行完,但CPU时间片耗尽
System.out.printf("[时间片用完] PID:%d 剩余时间:%d%n", pid, remainingTime);
return new RunResult(false, InterruptType.TIME_OUT); // 返回:未终止,触发时间片中断
}
逐步解析逻辑(结合 OS 状态转换):
- 设置运行状态:进程被调度后,从 "就绪" 转为 "运行",这是进程状态转换的关键一步。
- 模拟 CPU 执行 :
runTime = Math.min(timeSlice, remainingTime):比如时间片是 2,剩余时间是 3,则只运行 2 个单位(避免进程占用 CPU 超过时间片);若剩余时间是 1,则只运行 1 个单位(避免剩余时间为负)。Thread.sleep(runTime * 100):用线程休眠模拟 CPU 实际执行耗时(100ms 对应 1 个时间单位),让日志输出有先后顺序,更真实。
- I/O 阻塞判断 :
- 触发条件:
needIo为true(需要 I/O)且remainingTime < totalTime/2(执行到中途,比如总时间 8,剩余时间 3 时触发),模拟 "进程执行到一半需要读数据" 的场景。 - 状态转换:从 "运行"→"阻塞",并返回
IO_COMPLETE中断,后续 OS 会将进程放入阻塞队列。
- 触发条件:
- 运行完成判断 :剩余时间为 0 时,进程终止,状态转为
TERMINATED,不再参与调度。 - 时间片用完 :未执行完但时间片耗尽,返回
TIME_OUT中断,OS 会将进程放回就绪队列,等待下次调度(符合分时系统的时间片轮转逻辑)。
4.5 Getter/Setter:供 OS 访问进程属性
java
// Getter方法:OS需要获取进程的PID、名称、状态等属性
public int getPid() { return pid; }
public String getName() { return name; }
public ProcessState getState() { return state; }
// Setter方法:OS需要修改进程状态(如将阻塞的进程改为就绪)
public void setState(ProcessState state) { this.state = state; }
设计原因:
进程的属性(如pid、state)是private的(封装性),OS(OperatingSystem类)需要通过 Getter 获取属性、Setter 修改状态(如 I/O 完成后将进程状态从BLOCKED改为READY),所以提供这些方法。
五、操作系统内核模拟:OperatingSystem类(OS 的 "大脑")
OperatingSystem类是整个代码的核心,模拟真实操作系统的内核功能,包括进程管理、CPU 调度、中断处理、系统调用,相当于 OS 的 "控制中心"。
5.1 内核属性:模拟 OS 的核心数据结构
java
public class OperatingSystem {
private Queue<Process> readyQueue; // 就绪队列:存放"就绪"状态的进程(等待CPU),用LinkedList实现FIFO
private Queue<Process> blockedQueue; // 阻塞队列:存放"阻塞"状态的进程(等待I/O等资源),同样FIFO
private Process runningProcess; // 当前运行的进程:独占CPU的进程(同一时间只有一个)
private int timeSlice; // 时间片大小:分时系统中每个进程的CPU最大占用时间(默认2个单位)
private int pidCounter; // PID生成器:递增生成唯一PID(保证进程ID不重复)
属性设计的关键:
- 队列用
LinkedList实现 :Java 的LinkedList实现了Queue接口,支持poll()(取队首并删除)、add()(加队尾)等队列操作,且天然支持 FIFO(先进先出),符合就绪队列、阻塞队列的调度逻辑。 runningProcess的唯一性 :单 CPU 环境下,同一时间只有一个进程在运行,所以用单个Process对象表示,而非集合。pidCounter的作用 :每次创建进程时pidCounter++,保证每个进程的 PID 唯一(如第一个进程 PID=1,第二个 = 2)。
5.2 构造方法:初始化 OS 内核
java
public OperatingSystem(int timeSlice) {
this.readyQueue = new LinkedList<>(); // 初始化就绪队列(空)
this.blockedQueue = new LinkedList<>(); // 初始化阻塞队列(空)
this.runningProcess = null; // 初始无运行进程(CPU空闲)
this.timeSlice = timeSlice; // 传入时间片大小(如2)
this.pidCounter = 0; // PID从0开始递增
}
对应 OS 启动流程:
OS 启动时,就绪队列和阻塞队列都是空的,CPU 处于空闲状态,等待用户创建进程后开始调度。
5.3 进程创建:createProcess方法(OS 的进程创建功能)
java
public Process createProcess(String name, int totalTime, boolean needIo) {
pidCounter++; // 生成唯一PID(每次创建进程时递增)
// 创建进程实例:传入PID、名称、总时间、是否需要I/O
Process process = new Process(pidCounter, name, totalTime, needIo);
readyQueue.add(process); // 新进程初始为"就绪"状态,加入就绪队列
// 打印日志:告知用户进程创建成功
System.out.printf("[创建进程] PID:%d %s 加入就绪队列%n",
process.getPid(), process.getName());
return process; // 返回进程对象(后续可用于系统调用、中断处理)
}
对应 OS 操作:
比如用户在 Windows 中双击 "记事本",OS 会:
- 为记事本分配一个唯一 PID;
- 创建进程(加载程序代码和数据);
- 将进程放入就绪队列,等待 CPU 调度。这段代码完全模拟了这个过程。
5.4 CPU 调度:schedule方法(时间片轮转算法)
schedule是OperatingSystem的核心方法,模拟分时系统的时间片轮转调度------ 按 FIFO 顺序从就绪队列取进程,分配 CPU 时间片,执行后根据结果处理进程状态。
java
public void schedule() throws InterruptedException {
// 1. 检查就绪队列是否有进程(无进程则CPU空闲)
if (!readyQueue.isEmpty()) {
// 2. 从就绪队列取队首进程(FIFO):这是时间片轮转的核心------公平调度
runningProcess = readyQueue.poll();
System.out.printf("%n[调度进程] 选中PID:%d %s%n",
runningProcess.getPid(), runningProcess.getName());
// 3. 运行进程:调用Process的run方法,获取执行结果
Process.RunResult result = runningProcess.run(timeSlice);
// 4. 根据执行结果处理进程状态
if (!result.isTerminated()) { // 进程未终止
InterruptType interrupt = result.getInterruptType();
if (interrupt == InterruptType.TIME_OUT) {
// 4.1 时间片用完:进程回到就绪状态,放回就绪队列
runningProcess.setState(ProcessState.READY);
readyQueue.add(runningProcess);
} else if (interrupt == InterruptType.IO_COMPLETE) {
// 4.2 需要I/O:进程进入阻塞状态,放入阻塞队列
blockedQueue.add(runningProcess);
}
}
// 5. 释放CPU:当前进程执行完毕(无论是否终止),runningProcess设为null
runningProcess = null;
} else {
// 就绪队列为空:CPU空闲,打印提示
System.out.println("\n[调度提示] 就绪队列为空,CPU空闲");
}
}
时间片轮转调度的详细逻辑(结合 OS 理论):
- 公平性 :用
readyQueue.poll()取队首进程,保证每个就绪进程按顺序获得 CPU,符合分时系统 "公平对待每个用户" 的原则。 - 状态转换处理 :
- 时间片用完(
TIME_OUT):进程从 "运行"→"就绪",放回就绪队列,等待下次调度(比如进程 A 运行 2 个单位后时间片用完,回到队列末尾)。 - I/O 阻塞(
IO_COMPLETE):进程从 "运行"→"阻塞",放入阻塞队列,不再参与调度,直到 I/O 完成后被唤醒(比如浏览器加载网页时进入阻塞队列)。
- 时间片用完(
- CPU 释放 :无论进程是否终止,执行后都要将
runningProcess设为null,表示 CPU 空闲,等待下一次调度。
5.5 中断处理:handleInterrupt方法(OS 的中断响应)
真实 OS 中,中断是 "打破 CPU 执行流程的事件"(如 I/O 完成、程序错误),handleInterrupt方法模拟 OS 对不同中断的处理逻辑。
java
public void handleInterrupt(InterruptType interruptType, Process process) {
System.out.printf("%n[处理中断] 类型:%s%n", interruptType);
// 1. 处理I/O完成中断:唤醒阻塞的进程
if (interruptType == InterruptType.IO_COMPLETE && process != null) {
if (blockedQueue.contains(process)) { // 确保进程在阻塞队列中
blockedQueue.remove(process); // 从阻塞队列移除
process.setState(ProcessState.READY); // 状态转为"就绪"
readyQueue.add(process); // 加入就绪队列,等待调度
System.out.printf("[中断处理] PID:%d I/O完成,进入就绪队列%n",
process.getPid());
}
}
// 2. 处理除零异常中断:终止错误进程
else if (interruptType == InterruptType.DIVIDE_ZERO && process != null) {
process.setState(ProcessState.TERMINATED); // 状态转为"终止"
System.out.printf("[中断处理] PID:%d 发生除零异常,已终止%n",
process.getPid());
}
}
中断处理的核心逻辑:
- I/O 完成中断(
IO_COMPLETE) :- 场景:阻塞进程的 I/O 操作完成(如浏览器加载完网页),需要唤醒它继续执行。
- 流程:从阻塞队列移除→状态改为就绪→加入就绪队列,对应 OS 的 "阻塞→就绪" 状态转换。
- 除零异常(
DIVIDE_ZERO) :- 场景:进程执行错误(如
1/0),OS 需要终止它,避免影响其他进程。 - 流程:直接将进程状态设为
TERMINATED,不再参与调度,符合 OS 的 "错误处理" 逻辑。
- 场景:进程执行错误(如
5.6 系统调用:syscall方法(用户态→内核态切换)
用户程序不能直接操作硬件(如分配内存、读写文件),必须通过 "系统调用" 请求 OS 内核代为执行,syscall方法模拟这一过程。
java
public void syscall(Process process, String callType, Object... args) {
System.out.printf("%n[系统调用] PID:%d 请求:%s%n",
process.getPid(), callType);
// 模拟"用户态→内核态"切换:内核代用户执行特权操作
if (callType.equals("alloc_memory")) { // 内存分配系统调用
int size = (int) args[0]; // 获取请求的内存大小(如1024KB)
System.out.printf("[内核处理] 为PID:%d 分配%dKB内存%n",
process.getPid(), size);
} else if (callType.equals("read_file")) { // 文件读取系统调用
String filename = (String) args[0]; // 获取文件名(如"test.txt")
System.out.printf("[内核处理] 为PID:%d 读取文件:%s%n",
process.getPid(), filename);
}
// 模拟"内核态→用户态"切换:操作完成,返回用户程序
System.out.printf("[系统调用结束] PID:%d 操作完成%n", process.getPid());
}
系统调用的关键概念:
- 特权级切换:用户程序运行在 "用户态"(无硬件操作权限),系统调用时切换到 "内核态"(有硬件权限),内核执行操作后再切回用户态,这段代码用日志模拟了这个切换过程。
- 参数传递 :用
Object... args(可变参数)传递系统调用的参数(如内存大小、文件名),模拟真实 OS 中用户程序向内核传递参数的方式。 - 功能限制:这里只模拟了 "内存分配" 和 "文件读取" 两种系统调用,真实 OS 还有进程控制、设备管理等更多系统调用。
5.7 OS 主循环:run方法(OS 的持续运行)
真实 OS 启动后会持续运行,不断调度进程、处理中断,run方法模拟这个 "无限循环"(这里用指定时长限制循环)。
java
public void run(int duration) throws InterruptedException {
System.out.println("===== 操作系统启动 =====");
long startTime = System.currentTimeMillis(); // 记录OS启动时间
// 主循环:持续运行指定时长(duration秒)
while (System.currentTimeMillis() - startTime < duration * 1000) {
schedule(); // 1. 调度进程(分配CPU)
// 2. 随机唤醒阻塞进程(模拟I/O完成:每2秒内有50%概率唤醒)
if (!blockedQueue.isEmpty() && (System.currentTimeMillis() % 2000) < 1000) {
Process wakeProcess = blockedQueue.peek(); // 取阻塞队列首进程
handleInterrupt(InterruptType.IO_COMPLETE, wakeProcess); // 唤醒
}
}
System.out.println("\n===== 操作系统模拟结束 =====");
}
主循环的逻辑:
- 持续调度 :循环调用
schedule(),不断为就绪进程分配 CPU,保证 OS "不空闲"。 - 随机唤醒阻塞进程 :
System.currentTimeMillis() % 2000 < 1000:每 2 秒(2000ms)内,前 1 秒满足条件,模拟 "I/O 操作随机完成" 的场景(如硬盘读写完成时间不确定)。- 唤醒时调用
handleInterrupt,触发IO_COMPLETE中断,将阻塞进程转为就绪,符合真实 OS 中 I/O 完成后唤醒进程的逻辑。
- 循环终止 :当运行时间超过
duration秒(如 10 秒),OS 模拟结束,打印提示。
六、用户交互模拟:Main类(程序入口)
Main类是整个程序的入口,模拟用户使用操作系统的场景:创建进程、运行 OS、发起系统调用、触发异常,相当于用户在电脑上操作的过程。
java
public class Main {
public static void main(String[] args) throws InterruptedException {
// 1. 创建操作系统实例:时间片为2个单位(每个进程最多占CPU2个单位时间)
OperatingSystem os = new OperatingSystem(2);
// 2. 创建进程:模拟用户启动3个程序
os.createProcess("文本编辑器", 8, true); // I/O密集型(需I/O,总时间8)
os.createProcess("编译器", 5, false); // 计算密集型(无需I/O,总时间5)
os.createProcess("浏览器", 10, true); // I/O密集型(需I/O,总时间10)
// 3. 运行操作系统:模拟OS持续运行10秒
os.run(10);
// 4. 模拟用户程序发起系统调用:测试程序请求内存和文件操作
Process testProcess = os.createProcess("测试程序", 3, false);
os.syscall(testProcess, "alloc_memory", 1024); // 申请1024KB内存
os.syscall(testProcess, "read_file", "test.txt"); // 读取test.txt文件
// 5. 模拟程序发生除零异常:OS终止错误进程
os.handleInterrupt(InterruptType.DIVIDE_ZERO, testProcess);
}
}
用户操作流程解析:
- 启动 OS :创建
OperatingSystem实例,设置时间片为 2,相当于用户开机。 - 启动程序:创建 3 个不同类型的进程,模拟用户打开文本编辑器、编译器、浏览器。
- OS 运行 :调用
os.run(10),OS 开始 10 秒的调度和中断处理,用户可看到进程运行日志。 - 发起系统调用:测试程序请求内存分配和文件读取,模拟用户程序需要硬件资源时的操作。
- 处理异常:模拟测试程序发生除零错误,OS 终止它,保证系统稳定。
七、整体运行流程串联(从启动到结束)
- 初始化阶段:Main 类创建 OS 实例→创建 3 个进程→进程加入就绪队列。
- OS 运行阶段:OS 主循环启动→调用 schedule () 调度进程→进程运行(时间片用完 / 触发 I/O)→状态转换→随机唤醒阻塞进程→持续 10 秒。
- 后续操作:创建测试程序→发起系统调用→触发除零异常→OS 终止进程→程序结束。
整个流程完全模拟了真实操作系统的 "用户交互→进程管理→调度→中断→异常处理" 全链路,每个代码逻辑都能对应到 OS 的理论概念,是理解操作系统工作原理的绝佳示例。
八、关键 Java 语法与 OS 理论的结合点
| Java 语法特性 | 对应 OS 理论 | 代码中的应用场景 |
|---|---|---|
| 枚举(enum) | 进程状态、中断类型的离散性 | 定义 ProcessState、InterruptType,保证状态唯一 |
| 内部类(RunResult) | 进程运行结果的关联性 | 封装 run 方法的返回值(是否终止 + 中断类型) |
| LinkedList 实现 Queue | 就绪队列、阻塞队列的 FIFO 特性 | 存储就绪 / 阻塞进程,支持调度时的取队首、加队尾 |
| Thread.sleep | CPU 执行耗时、I/O 等待时间 | 模拟进程运行、I/O 阻塞的耗时 |
| 可变参数(Object...) | 系统调用的参数传递 | 传递内存大小、文件名等系统调用参数 |
| throws InterruptedException | 线程休眠可能被中断 | run 方法、schedule 方法声明异常,处理 sleep 的中断 |
九、Java完整代码展示
先新建一个项目,然后在同一目录下依次命名上述类,如图所示。


java
public enum ProcessState {// 进程状态枚举
READY("就绪"),
RUNNING("运行"),
BLOCKED("阻塞"),
TERMINATED("终止");
private final String description;
ProcessState(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}

java
public enum InterruptType {// 中断类型枚举
IO_COMPLETE("I/O完成中断"),
TIME_OUT("时间片用完"),
DIVIDE_ZERO("除零异常"),
SYSTEM_CALL("系统调用");
private final String description;
InterruptType(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}

java
public class Process {// 进程类
private int pid; // 进程ID
private String name; // 进程名称
private int totalTime; // 总运行时间
private int remainingTime; // 剩余运行时间
private ProcessState state; // 进程状态
private boolean needIo; // 是否需要I/O操作
private int ioRemaining; // 剩余I/O时间
// 构造方法:初始化进程属性
public Process(int pid, String name, int totalTime, boolean needIo) {
this.pid = pid;
this.name = name;
this.totalTime = totalTime;
this.remainingTime = totalTime;
this.state = ProcessState.READY; // 初始状态为就绪
this.needIo = needIo;
this.ioRemaining = needIo ? 2 : 0; // I/O需要2个时间单位
}
// 内部类:封装run方法的返回结果(是否终止+中断类型)
public static class RunResult {
private boolean isTerminated;
private InterruptType interruptType;
public RunResult(boolean isTerminated, InterruptType interruptType) {
this.isTerminated = isTerminated;
this.interruptType = interruptType;
}
public boolean isTerminated() { return isTerminated; }
public InterruptType getInterruptType() { return interruptType; }
}
// 进程运行逻辑:模拟CPU执行过程
public RunResult run(int timeSlice) throws InterruptedException {
this.state = ProcessState.RUNNING;
System.out.printf("[进程运行] PID:%d %s 开始运行,剩余时间:%d%n",
pid, name, remainingTime);
// 模拟CPU执行时间(取时间片和剩余时间的最小值)
int runTime = Math.min(timeSlice, remainingTime);
Thread.sleep(runTime * 100); // 延迟100ms/单位时间(加速模拟)
remainingTime -= runTime;
// 检查是否需要I/O(触发阻塞)
if (needIo && remainingTime < totalTime / 2) {
if (ioRemaining > 0) {
this.state = ProcessState.BLOCKED;
ioRemaining--;
System.out.printf("[进程阻塞] PID:%d 等待I/O,剩余I/O时间:%d%n",
pid, ioRemaining);
return new RunResult(false, InterruptType.IO_COMPLETE);
}
}
// 检查是否运行完成
if (remainingTime <= 0) {
this.state = ProcessState.TERMINATED;
System.out.printf("[进程终止] PID:%d %s 执行完成%n", pid, name);
return new RunResult(true, null);
}
// 时间片用完(触发时钟中断)
System.out.printf("[时间片用完] PID:%d 剩余时间:%d%n", pid, remainingTime);
return new RunResult(false, InterruptType.TIME_OUT);
}
// Getter和Setter(供操作系统类访问属性)
public int getPid() { return pid; }
public String getName() { return name; }
public ProcessState getState() { return state; }
public void setState(ProcessState state) { this.state = state; }
}

java
import java.util.LinkedList;
import java.util.Queue;
public class OperatingSystem {// 操作系统核心类
private Queue<Process> readyQueue; // 就绪队列(等待CPU的进程)
private Queue<Process> blockedQueue; // 阻塞队列(等待资源的进程)
private Process runningProcess; // 当前运行的进程
private int timeSlice; // 时间片大小
private int pidCounter; // PID生成器
// 构造方法:初始化操作系统核心组件
public OperatingSystem(int timeSlice) {
this.readyQueue = new LinkedList<>();
this.blockedQueue = new LinkedList<>();
this.runningProcess = null;
this.timeSlice = timeSlice;
this.pidCounter = 0;
}
// 创建进程:生成唯一PID并加入就绪队列
public Process createProcess(String name, int totalTime, boolean needIo) {
pidCounter++;
Process process = new Process(pidCounter, name, totalTime, needIo);
readyQueue.add(process);
System.out.printf("[创建进程] PID:%d %s 加入就绪队列%n",
process.getPid(), process.getName());
return process;
}
// 进程调度:时间片轮转算法分配CPU
public void schedule() throws InterruptedException {
if (!readyQueue.isEmpty()) {
// 从就绪队列选取下一个进程
runningProcess = readyQueue.poll();
System.out.printf("%n[调度进程] 选中PID:%d %s%n",
runningProcess.getPid(), runningProcess.getName());
// 运行进程并处理结果
Process.RunResult result = runningProcess.run(timeSlice);
if (!result.isTerminated()) {
InterruptType interrupt = result.getInterruptType();
if (interrupt == InterruptType.TIME_OUT) {
// 时间片用完,放回就绪队列
runningProcess.setState(ProcessState.READY);
readyQueue.add(runningProcess);
} else if (interrupt == InterruptType.IO_COMPLETE) {
// 需要I/O,放入阻塞队列
blockedQueue.add(runningProcess);
}
}
runningProcess = null; // 释放CPU
} else {
System.out.println("\n[调度提示] 就绪队列为空,CPU空闲");
}
}
// 中断处理:响应外部事件(如I/O完成、程序异常)
public void handleInterrupt(InterruptType interruptType, Process process) {
System.out.printf("%n[处理中断] 类型:%s%n", interruptType);
if (interruptType == InterruptType.IO_COMPLETE && process != null) {
// I/O完成:阻塞进程移至就绪队列
if (blockedQueue.contains(process)) {
blockedQueue.remove(process);
process.setState(ProcessState.READY);
readyQueue.add(process);
System.out.printf("[中断处理] PID:%d I/O完成,进入就绪队列%n",
process.getPid());
}
} else if (interruptType == InterruptType.DIVIDE_ZERO && process != null) {
// 除零异常:终止进程
process.setState(ProcessState.TERMINATED);
System.out.printf("[中断处理] PID:%d 发生除零异常,已终止%n",
process.getPid());
}
}
// 系统调用:模拟用户程序请求内核服务
public void syscall(Process process, String callType, Object... args) {
System.out.printf("%n[系统调用] PID:%d 请求:%s%n",
process.getPid(), callType);
if (callType.equals("alloc_memory")) {
int size = (int) args[0];
System.out.printf("[内核处理] 为PID:%d 分配%dKB内存%n",
process.getPid(), size);
} else if (callType.equals("read_file")) {
String filename = (String) args[0];
System.out.printf("[内核处理] 为PID:%d 读取文件:%s%n",
process.getPid(), filename);
}
System.out.printf("[系统调用结束] PID:%d 操作完成%n", process.getPid());
}
// 操作系统主循环:持续调度进程并处理事件
public void run(int duration) throws InterruptedException {
System.out.println("===== 操作系统启动 =====");
long startTime = System.currentTimeMillis();
// 持续运行指定时长(单位:秒)
while (System.currentTimeMillis() - startTime < duration * 1000) {
schedule(); // 调度进程
// 随机唤醒阻塞进程(模拟I/O完成)
if (!blockedQueue.isEmpty() && (System.currentTimeMillis() % 2000) < 1000) {
Process wakeProcess = blockedQueue.peek();
handleInterrupt(InterruptType.IO_COMPLETE, wakeProcess);
}
}
System.out.println("\n===== 操作系统模拟结束 =====");
}
}

java
public class Main {// 主程序
public static void main(String[] args) throws InterruptedException {
// 创建操作系统实例(时间片为2个单位)
OperatingSystem os = new OperatingSystem(2);
// 创建进程(多道程序环境)
os.createProcess("文本编辑器", 8, true); // I/O密集型
os.createProcess("编译器", 5, false); // 计算密集型
os.createProcess("浏览器", 10, true); // I/O密集型
// 运行操作系统(模拟10秒)
os.run(10);
// 模拟系统调用
Process testProcess = os.createProcess("测试程序", 3, false);
os.syscall(testProcess, "alloc_memory", 1024); // 申请内存
os.syscall(testProcess, "read_file", "test.txt"); // 读取文件
// 模拟除零异常(内部中断)
os.handleInterrupt(InterruptType.DIVIDE_ZERO, testProcess);
}
}
十、程序运行结果展示
bash
===== 操作系统启动 =====
[创建进程] PID:1 文本编辑器 加入就绪队列
[创建进程] PID:2 编译器 加入就绪队列
[创建进程] PID:3 浏览器 加入就绪队列
[调度进程] 选中PID:1 文本编辑器
[进程运行] PID:1 文本编辑器 开始运行,剩余时间:8
[时间片用完] PID:1 剩余时间:6
[调度进程] 选中PID:2 编译器
[进程运行] PID:2 编译器 开始运行,剩余时间:5
[时间片用完] PID:2 剩余时间:3
...(中间省略多次调度和I/O中断处理)...
===== 操作系统模拟结束 =====
[创建进程] PID:4 测试程序 加入就绪队列
[系统调用] PID:4 请求:alloc_memory
[内核处理] 为PID:4 分配1024KB内存
[系统调用结束] PID:4 操作完成
[系统调用] PID:4 请求:read_file
[内核处理] 为PID:4 读取文件:test.txt
[系统调用结束] PID:4 操作完成
[处理中断] 类型:除零异常
[中断处理] PID:4 发生除零异常,已终止
十一、总结
本文通过Java代码模拟操作系统核心机制,包括进程管理、CPU调度、中断处理和系统调用。代码采用四层架构:基础数据定义层(枚举类定义进程状态和中断类型)、进程实体层(Process类模拟PCB)、内核核心层(OperatingSystem类实现调度和中断处理)和用户交互层(Main类模拟用户操作)。重点展示了进程状态转换、时间片轮转调度算法、I/O中断处理以及系统调用实现,完整复现了操作系统从进程创建到终止的全生命周期管理流程。通过对象封装和队列管理,生动演示了操作系统的多道程序环境和并发控制原理。