【操作系统】模拟真实操作系统核心功能的Java实现

目录

一、引言

[二、整体架构总览:模拟操作系统的 "四层组件"](#二、整体架构总览:模拟操作系统的 “四层组件”)

三、基础数据定义:枚举类(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 的持续运行))

主循环的逻辑:

六、用户交互模拟:Main类(程序入口)

用户操作流程解析:

七、整体运行流程串联(从启动到结束)

[八、关键 Java 语法与 OS 理论的结合点](#八、关键 Java 语法与 OS 理论的结合点)

九、Java完整代码展示

十、程序运行结果展示

十一、总结


一、引言

操作系统的核心机制:进程管理(状态转换)、CPU 调度(时间片轮转)、中断处理(响应突发事件)、系统调用(用户与内核交互)。其详细讲解可以参考【操作系统】计算机系统概述-CSDN博客。下面分 4 个核心模块,从 "是什么→为什么这么设计→具体怎么实现" 的逻辑详细讲解。

二、整体架构总览:模拟操作系统的 "四层组件"

这段代码通过 4 个核心部分,完整复现了操作系统的 "进程 - 调度 - 中断 - 用户交互" 流程,各组件对应关系如下:

代码组件 对应真实操作系统模块 核心作用
ProcessState/InterruptType 基础数据定义层 定义进程状态、中断类型(避免魔法值,保证一致性)
Process 进程实体层(PCB) 模拟进程的属性(PID、状态、运行时间)和行为(运行、阻塞)
OperatingSystem 内核核心层 实现进程调度、中断处理、系统调用(OS 的 "大脑")
Main 用户交互层 模拟用户使用 OS 的场景(创建进程、发起请求)

先从最基础的 "数据定义层" 开始,再逐步深入核心逻辑。

三、基础数据定义:枚举类(ProcessStateInterruptType

真实操作系统中,"进程状态" 和 "中断类型" 是固定的离散值(如进程只能是就绪 / 运行 / 阻塞 / 终止,中断只能是 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;
    }
}
关键设计点解析:
  1. 为什么需要description属性? 枚举本身的名称(如READY)是英文,description存储中文描述,方便日志输出(比如打印 "进程状态:就绪" 而非 "进程状态:READY",更易读)。
  2. 枚举构造方法的访问修饰符? Java 枚举的构造方法默认是private,只能在枚举内部初始化值(如READY("就绪")),外部无法创建枚举实例,保证状态的唯一性。
  3. 对应 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;
    }
}
关键设计点解析:
  1. 中断类型的分类逻辑 :分为 "外部中断"(IO_COMPLETETIME_OUT,来自 CPU 外部设备)和 "内部异常"(DIVIDE_ZEROSYSTEM_CALL,来自 CPU 执行内部),对应真实 OS 的中断分类。
  2. 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的唯一性 :后续由OperatingSystempidCounter递增生成,保证每个进程 PID 唯一。
  • remainingTime的核心作用 :是判断进程是否执行完成的依据(remainingTime <= 0则终止)。
  • needIoioRemaining:模拟真实进程的 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方法需要返回两个关键信息:

  1. 进程是否执行完成(isTerminated);
  2. 若未完成,触发了哪种中断(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 状态转换):
  1. 设置运行状态:进程被调度后,从 "就绪" 转为 "运行",这是进程状态转换的关键一步。
  2. 模拟 CPU 执行
    • runTime = Math.min(timeSlice, remainingTime):比如时间片是 2,剩余时间是 3,则只运行 2 个单位(避免进程占用 CPU 超过时间片);若剩余时间是 1,则只运行 1 个单位(避免剩余时间为负)。
    • Thread.sleep(runTime * 100):用线程休眠模拟 CPU 实际执行耗时(100ms 对应 1 个时间单位),让日志输出有先后顺序,更真实。
  3. I/O 阻塞判断
    • 触发条件:needIo为true(需要 I/O)且remainingTime < totalTime/2(执行到中途,比如总时间 8,剩余时间 3 时触发),模拟 "进程执行到一半需要读数据" 的场景。
    • 状态转换:从 "运行"→"阻塞",并返回IO_COMPLETE中断,后续 OS 会将进程放入阻塞队列。
  4. 运行完成判断 :剩余时间为 0 时,进程终止,状态转为TERMINATED,不再参与调度。
  5. 时间片用完 :未执行完但时间片耗尽,返回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; }
设计原因:

进程的属性(如pidstate)是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 会:

  1. 为记事本分配一个唯一 PID;
  2. 创建进程(加载程序代码和数据);
  3. 将进程放入就绪队列,等待 CPU 调度。这段代码完全模拟了这个过程。

5.4 CPU 调度:schedule方法(时间片轮转算法)

scheduleOperatingSystem的核心方法,模拟分时系统的时间片轮转调度------ 按 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 理论):
  1. 公平性 :用readyQueue.poll()取队首进程,保证每个就绪进程按顺序获得 CPU,符合分时系统 "公平对待每个用户" 的原则。
  2. 状态转换处理
    • 时间片用完(TIME_OUT):进程从 "运行"→"就绪",放回就绪队列,等待下次调度(比如进程 A 运行 2 个单位后时间片用完,回到队列末尾)。
    • I/O 阻塞(IO_COMPLETE):进程从 "运行"→"阻塞",放入阻塞队列,不再参与调度,直到 I/O 完成后被唤醒(比如浏览器加载网页时进入阻塞队列)。
  3. 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());
    }
}
中断处理的核心逻辑:
  1. I/O 完成中断(IO_COMPLETE
    • 场景:阻塞进程的 I/O 操作完成(如浏览器加载完网页),需要唤醒它继续执行。
    • 流程:从阻塞队列移除→状态改为就绪→加入就绪队列,对应 OS 的 "阻塞→就绪" 状态转换。
  2. 除零异常(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());
}
系统调用的关键概念:
  1. 特权级切换:用户程序运行在 "用户态"(无硬件操作权限),系统调用时切换到 "内核态"(有硬件权限),内核执行操作后再切回用户态,这段代码用日志模拟了这个切换过程。
  2. 参数传递 :用Object... args(可变参数)传递系统调用的参数(如内存大小、文件名),模拟真实 OS 中用户程序向内核传递参数的方式。
  3. 功能限制:这里只模拟了 "内存分配" 和 "文件读取" 两种系统调用,真实 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===== 操作系统模拟结束 =====");
}
主循环的逻辑:
  1. 持续调度 :循环调用schedule(),不断为就绪进程分配 CPU,保证 OS "不空闲"。
  2. 随机唤醒阻塞进程
    • System.currentTimeMillis() % 2000 < 1000:每 2 秒(2000ms)内,前 1 秒满足条件,模拟 "I/O 操作随机完成" 的场景(如硬盘读写完成时间不确定)。
    • 唤醒时调用handleInterrupt,触发IO_COMPLETE中断,将阻塞进程转为就绪,符合真实 OS 中 I/O 完成后唤醒进程的逻辑。
  3. 循环终止 :当运行时间超过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);
    }
}
用户操作流程解析:
  1. 启动 OS :创建OperatingSystem实例,设置时间片为 2,相当于用户开机。
  2. 启动程序:创建 3 个不同类型的进程,模拟用户打开文本编辑器、编译器、浏览器。
  3. OS 运行 :调用os.run(10),OS 开始 10 秒的调度和中断处理,用户可看到进程运行日志。
  4. 发起系统调用:测试程序请求内存分配和文件读取,模拟用户程序需要硬件资源时的操作。
  5. 处理异常:模拟测试程序发生除零错误,OS 终止它,保证系统稳定。

七、整体运行流程串联(从启动到结束)

  1. 初始化阶段:Main 类创建 OS 实例→创建 3 个进程→进程加入就绪队列。
  2. OS 运行阶段:OS 主循环启动→调用 schedule () 调度进程→进程运行(时间片用完 / 触发 I/O)→状态转换→随机唤醒阻塞进程→持续 10 秒。
  3. 后续操作:创建测试程序→发起系统调用→触发除零异常→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中断处理以及系统调用实现,完整复现了操作系统从进程创建到终止的全生命周期管理流程。通过对象封装和队列管理,生动演示了操作系统的多道程序环境和并发控制原理。

相关推荐
程序员皮皮林3 小时前
Java 25 正式发布:更简洁、更高效、更现代!
java·开发语言·python
好家伙VCC3 小时前
**发散创新:AI绘画编程探索与实践**随着人工智能技术的飞速发展,AI绘
java·人工智能·python·ai作画
勇者无畏4043 小时前
基于 Spring AI Alibaba 搭建 Text-To-SQL 智能系统(前置介绍)
java·后端·spring·prompt·embedding
练习时长一年3 小时前
IDEA开发常用快捷键总结
java·ide·intellij-idea
温柔53293 小时前
仓颉语言异常捕获机制深度解析
java·服务器·前端
运维李哥不背锅3 小时前
Ansible 的变量与模板:实现更灵活的自动化配置
java·自动化·ansible
信码由缰3 小时前
Java 21 虚拟线程 vs 缓存线程池与固定线程池
java
踩坑小念3 小时前
进程 线程 协程基本概念和区别 还有内在联系
java·linux·jvm·操作系统
yyongsheng3 小时前
SpringBoot项目集成easy-es框架
java·服务器·前端