《计算机操作系统》第六章-输入输出系统

前言

大家好!今天给大家带来《计算机操作系统》第六章 ------ 输入输出(I/O)系统的全面解析。I/O 系统是操作系统连接外部设备和计算机核心的桥梁,也是面试、考研的高频考点。本文会从基础概念到代码实现,用通俗易懂的语言拆解每个知识点,还附带完整可运行的 C++98 代码案例、架构图 / 流程图,方便大家动手实操,彻底吃透 I/O 系统!

一、本章知识思维导图

6.1 I/O 系统的功能、模型和接口

核心概念

I/O 系统是操作系统管理所有输入输出设备的子系统,核心目标是高效、统一地完成 CPU 与外部设备的数据交互。

1. 核心功能
  • 数据传输:在 CPU / 内存与外设之间搬运数据;
  • 设备控制:控制设备的启动、停止、状态检测;
  • 错误处理:检测并处理设备故障(如磁盘坏道、网络中断);
  • 设备分配:为进程分配独占 / 共享设备;
  • 缓冲管理:通过缓冲区缓解 CPU 与外设的速度差异。
2. 4 种 I/O 控制模型(从低效到高效)
模型 核心特点 适用场景
程序 I/O(轮询) CPU 循环查询设备状态,直到完成 简单外设(如 LED 灯)
中断驱动 I/O 设备完成后发中断,CPU 响应处理 键盘、鼠标等低速设备
DMA(直接内存访问) 专用 DMA 控制器接管数据传输,CPU 仅初始化 磁盘、网卡等高速设备
通道 I/O 通道执行 I/O 指令,CPU 完全解放 大型机、多外设场景
3. I/O 接口
  • 硬件接口:设备与控制器的物理连接(如 USB、PCI);
  • 编程接口:操作系统提供给应用程序的调用接口(如 read/write 系统调用)。

架构图

代码案例:程序 I/O(轮询)vs 中断模拟(C++98)

复制代码
#include <iostream>
#include <unistd.h> // 用于sleep函数(Linux)
// 兼容Windows:#include <windows.h>,替换sleep为Sleep(1000)
using namespace std;

// 模拟设备状态
enum DeviceState { IDLE, BUSY, COMPLETE };
DeviceState deviceState = IDLE;

// ------------- 6.1 程序I/O(轮询)实现 -------------
void programIO() {
    cout << "【程序I/O】开始轮询设备状态..." << endl;
    // 模拟设备开始工作
    deviceState = BUSY;
    int count = 0;
    // CPU循环查询(轮询),直到设备完成
    while (deviceState != COMPLETE) {
        count++;
        cout << "轮询第" << count << "次,设备状态:忙" << endl;
        sleep(1); // 模拟CPU等待
        // 模拟设备完成工作
        if (count == 3) {
            deviceState = COMPLETE;
            cout << "设备完成!轮询结束" << endl;
        }
    }
    deviceState = IDLE; // 重置状态
}

// ------------- 6.1 中断驱动I/O模拟 -------------
bool isInterrupt = false; // 中断标志

// 模拟设备工作线程(实际由硬件触发中断)
void deviceWork() {
    cout << "【中断驱动I/O】设备开始工作..." << endl;
    sleep(3); // 模拟设备工作耗时
    isInterrupt = true; // 触发中断
    cout << "设备完成,发送中断信号!" << endl;
}

// 中断处理程序
void interruptHandler() {
    cout << "CPU响应中断:处理设备数据..." << endl;
    // 模拟数据处理
    sleep(1);
    cout << "中断处理完成!" << endl;
    isInterrupt = false; // 清除中断标志
}

int main() {
    // 测试程序I/O
    programIO();
    cout << "------------------------" << endl;
    // 测试中断驱动I/O
    deviceWork();
    // CPU执行其他任务,直到收到中断
    while (!isInterrupt) {
        cout << "CPU执行其他任务中..." << endl;
        sleep(1);
    }
    // 响应中断
    interruptHandler();

    return 0;
}
代码说明:
  1. 兼容 C++98 标准,无 C++11 及以上特性(如 auto、lambda);
  2. programIO函数模拟轮询模式:CPU 空等设备,效率低;
  3. interruptHandler模拟中断处理:CPU 可执行其他任务,设备完成后触发中断再处理,效率更高;
  4. 运行环境:Linux(直接编译)、Windows(替换 sleep 为 Sleep 即可);
  5. 编译命令(Linux):g++ -std=c++98 io_system.cpp -o io_system && ./io_system

6.2 I/O 设备和设备控制器

核心概念

1. I/O 设备分类
  • 字符设备:按字符逐个传输(键盘、鼠标、打印机),不可随机访问;
  • 块设备:按固定大小 "块" 传输(磁盘、U 盘),可随机访问;
  • 网络设备:面向数据包传输(网卡)。
2. 设备控制器(硬件核心)

设备控制器是 CPU 与外设之间的 "中间人",负责:

  • 接收 CPU 的指令(如读 / 写);
  • 控制外设的具体操作;
  • 向 CPU 反馈设备状态;
  • 数据缓冲(缓解 CPU 与外设速度差)。

架构图

代码案例:设备控制器模拟(C++98)

复制代码
#include <iostream>
#include <string>
using namespace std;

// 模拟设备控制器寄存器
struct DeviceController {
    // 数据寄存器:存储待传输的数据
    string dataReg;
    // 命令寄存器:存储CPU下发的命令(READ/WRITE/STOP)
    string cmdReg;
    // 状态寄存器:反馈设备状态(READY/BUSY/ERROR)
    string statusReg;

    // 初始化控制器
    DeviceController() : statusReg("READY") {}

    // 接收CPU命令
    void setCommand(const string& cmd) {
        if (statusReg == "READY") {
            cmdReg = cmd;
            statusReg = "BUSY";
            cout << "控制器接收命令:" << cmd << endl;
        } else {
            cout << "设备忙,无法接收命令!" << endl;
        }
    }

    // 执行命令(模拟设备操作)
    void executeCommand(const string& data = "") {
        if (cmdReg == "READ") {
            // 模拟从设备读取数据到数据寄存器
            dataReg = "DEVICE_DATA_123";
            cout << "读取数据完成:" << dataReg << endl;
        } else if (cmdReg == "WRITE") {
            // 模拟将数据写入设备
            dataReg = data;
            cout << "写入数据完成:" << dataReg << endl;
        } else if (cmdReg == "STOP") {
            cout << "设备停止工作" << endl;
        } else {
            statusReg = "ERROR";
            cout << "无效命令!状态:ERROR" << endl;
            return;
        }
        // 执行完成,重置状态
        cmdReg = "";
        statusReg = "READY";
    }

    // 获取设备状态
    string getStatus() {
        return statusReg;
    }
};

int main() {
    DeviceController diskController; // 模拟磁盘控制器
    cout << "初始状态:" << diskController.getStatus() << endl;

    // CPU下发读命令
    diskController.setCommand("READ");
    diskController.executeCommand();
    cout << "操作后状态:" << diskController.getStatus() << endl;

    // CPU下发写命令
    diskController.setCommand("WRITE");
    diskController.executeCommand("USER_FILE_DATA");
    cout << "操作后状态:" << diskController.getStatus() << endl;

    // 设备忙时下发命令(测试异常)
    diskController.setCommand("READ");
    diskController.setCommand("WRITE"); // 此时设备忙,无法接收

    return 0;
}
代码说明:
  1. 模拟设备控制器的核心寄存器(数据、命令、状态);
  2. 实现命令接收、执行、状态反馈的完整逻辑;
  3. 包含异常处理(设备忙时拒绝命令),符合实际控制器行为;
  4. 编译命令:g++ -std=c++98 device_controller.cpp -o device_controller && ./device_controller

6.3 中断机构和中断处理程序

核心概念

中断是外设 "主动通知" CPU 的机制,是 I/O 系统的核心触发方式:

  1. 中断源:引发中断的事件(设备完成、故障、时钟等);
  2. 中断优先级:高优先级中断可打断低优先级(如故障中断 > 普通 I/O 中断);
  3. 中断处理流程:

代码案例:中断处理程序模拟(C++98)

复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 中断类型及优先级(数值越大优先级越高)
enum InterruptType {
    IO_INTERRUPT = 1,    // I/O完成中断
    ERROR_INTERRUPT = 3, // 设备故障中断
    TIMER_INTERRUPT = 2  // 时钟中断
};

// 中断请求结构体
struct InterruptRequest {
    InterruptType type;
    string deviceName;
    int priority;

    InterruptRequest(InterruptType t, const string& name) 
        : type(t), deviceName(name) {
        // 初始化优先级
        priority = static_cast<int>(t);
    }
};

// 中断控制器(管理中断队列、优先级)
class InterruptController {
private:
    vector<InterruptRequest> interruptQueue; // 中断请求队列
    bool isHandling; // 是否正在处理中断

    // 找到队列中优先级最高的中断
    int findHighestPriorityIdx() {
        int maxPriority = -1;
        int idx = -1;
        for (int i = 0; i < interruptQueue.size(); ++i) {
            if (interruptQueue[i].priority > maxPriority) {
                maxPriority = interruptQueue[i].priority;
                idx = i;
            }
        }
        return idx;
    }

    // 中断处理函数(针对不同类型)
    void handleIOInterrupt(const string& dev) {
        cout << "处理I/O中断:设备[" << dev << "]完成数据传输" << endl;
    }

    void handleErrorInterrupt(const string& dev) {
        cout << "处理故障中断:设备[" << dev << "]发生故障,紧急处理" << endl;
    }

    void handleTimerInterrupt(const string& dev) {
        cout << "处理时钟中断:定时任务触发" << endl;
    }

public:
    InterruptController() : isHandling(false) {}

    // 注册中断请求
    void registerInterrupt(const InterruptRequest& irq) {
        interruptQueue.push_back(irq);
        cout << "注册中断:" << devType(irq.type) << "(设备:" << irq.deviceName << ")" << endl;
        // 若未处理中断,立即调度
        if (!isHandling) {
            dispatchInterrupt();
        }
    }

    // 调度并处理最高优先级中断
    void dispatchInterrupt() {
        isHandling = true;
        int idx = findHighestPriorityIdx();
        if (idx == -1) {
            isHandling = false;
            return;
        }
        InterruptRequest irq = interruptQueue[idx];
        // 移除已处理的中断
        interruptQueue.erase(interruptQueue.begin() + idx);

        // 处理中断
        switch (irq.type) {
            case IO_INTERRUPT:
                handleIOInterrupt(irq.deviceName);
                break;
            case ERROR_INTERRUPT:
                handleErrorInterrupt(irq.deviceName);
                break;
            case TIMER_INTERRUPT:
                handleTimerInterrupt(irq.deviceName);
                break;
            default:
                cout << "未知中断类型" << endl;
        }

        isHandling = false;
        // 递归处理剩余中断
        if (!interruptQueue.empty()) {
            dispatchInterrupt();
        }
    }

    // 辅助函数:中断类型转字符串
    string devType(InterruptType t) {
        switch (t) {
            case IO_INTERRUPT: return "I/O中断";
            case ERROR_INTERRUPT: return "故障中断";
            case TIMER_INTERRUPT: return "时钟中断";
            default: return "未知中断";
        }
    }
};

int main() {
    InterruptController ic;

    // 模拟多个中断请求(不同优先级)
    ic.registerInterrupt(InterruptRequest(IO_INTERRUPT, "键盘"));
    ic.registerInterrupt(InterruptRequest(ERROR_INTERRUPT, "磁盘")); // 高优先级,先处理
    ic.registerInterrupt(InterruptRequest(TIMER_INTERRUPT, "系统时钟"));

    return 0;
}
代码说明:
  1. 模拟中断控制器的核心逻辑:中断注册、优先级排序、中断调度;
  2. 实现不同类型中断的差异化处理,符合实际系统的中断优先级规则;
  3. 兼容 C++98(使用 vector 而非 C++11 的容器特性);
  4. 编译命令:g++ -std=c++98 interrupt.cpp -o interrupt && ./interrupt

6.4 设备驱动程序

核心概念

设备驱动程序是操作系统中与设备紧密相关的底层软件,是硬件和操作系统之间的 "翻译官":

  • 核心作用:将操作系统的通用 I/O 指令,转换为设备能识别的具体指令;
  • 设计原则:与设备相关、与操作系统无关;
  • 核心流程:初始化→接收请求→设备操作→中断处理→释放资源。

架构图

代码案例:磁盘设备驱动模拟(C++98)

复制代码
#include <iostream>
#include <string>
#include <map>
using namespace std;

// 模拟磁盘硬件
struct DiskHardware {
    map<int, string> blocks; // 磁盘块:块号→数据
    bool isWorking;

    DiskHardware() : isWorking(true) {
        // 初始化磁盘块数据
        blocks[0] = "系统文件";
        blocks[1] = "用户数据1";
        blocks[2] = "用户数据2";
    }

    // 读磁盘块
    string readBlock(int blockNum) {
        if (!isWorking) return "设备故障";
        if (blocks.find(blockNum) != blocks.end()) {
            return blocks[blockNum];
        }
        return "块不存在";
    }

    // 写磁盘块
    bool writeBlock(int blockNum, const string& data) {
        if (!isWorking) return false;
        blocks[blockNum] = data;
        return true;
    }

    // 模拟设备故障
    void setFault() {
        isWorking = false;
    }
};

// 磁盘设备驱动程序
class DiskDriver {
private:
    DiskHardware disk; // 关联的硬件设备
    string driverName;

    // 驱动内部:校验参数
    bool validateParam(int blockNum) {
        if (blockNum < 0 || blockNum > 100) { // 模拟块号范围
            cout << "[" << driverName << "] 参数错误:块号超出范围" << endl;
            return false;
        }
        return true;
    }

public:
    DiskDriver(const string& name) : driverName(name) {}

    // 初始化驱动
    bool init() {
        cout << "[" << driverName << "] 驱动初始化成功" << endl;
        return true;
    }

    // 对外提供的读接口(操作系统调用)
    string read(int blockNum) {
        cout << "[" << driverName << "] 接收读请求,块号:" << blockNum << endl;
        // 1. 参数校验
        if (!validateParam(blockNum)) {
            return "读失败";
        }
        // 2. 转换为硬件指令,操作硬件
        string data = disk.readBlock(blockNum);
        // 3. 返回结果给操作系统
        cout << "[" << driverName << "] 读完成,数据:" << data << endl;
        return data;
    }

    // 对外提供的写接口(操作系统调用)
    bool write(int blockNum, const string& data) {
        cout << "[" << driverName << "] 接收写请求,块号:" << blockNum << ",数据:" << data << endl;
        // 1. 参数校验
        if (!validateParam(blockNum)) {
            return false;
        }
        // 2. 转换为硬件指令,操作硬件
        bool ret = disk.writeBlock(blockNum, data);
        // 3. 返回结果给操作系统
        if (ret) {
            cout << "[" << driverName << "] 写完成" << endl;
        } else {
            cout << "[" << driverName << "] 写失败:设备故障" << endl;
        }
        return ret;
    }

    // 处理设备中断
    void handleInterrupt(const string& type) {
        if (type == "READ_COMPLETE") {
            cout << "[" << driverName << "] 读中断:数据传输完成" << endl;
        } else if (type == "WRITE_COMPLETE") {
            cout << "[" << driverName << "] 写中断:数据写入完成" << endl;
        } else if (type == "ERROR") {
            cout << "[" << driverName << "] 故障中断:设备异常" << endl;
            disk.setFault();
        }
    }
};

int main() {
    DiskDriver driver("SATA磁盘驱动");
    driver.init();

    // 测试读操作
    driver.read(1);
    // 测试写操作
    driver.write(3, "新用户数据");
    // 测试故障中断
    driver.handleInterrupt("ERROR");
    // 故障后写操作(测试失败)
    driver.write(4, "测试数据");

    return 0;
}
代码说明:
  1. 模拟磁盘驱动的完整生命周期:初始化→接收读写请求→参数校验→硬件操作→中断处理;
  2. 包含故障模拟和异常处理,贴近实际驱动程序逻辑;
  3. 编译命令:g++ -std=c++98 device_driver.cpp -o device_driver && ./device_driver

6.5 与设备无关的 I/O 软件

核心概念

与设备无关的 I/O 软件(设备独立性软件)是操作系统 I/O 层的核心,目标是屏蔽设备差异,让应用程序使用 "逻辑设备名"(如 "硬盘")而非 "物理设备名"(如 /dev/sda1):

  • 核心功能:
    1. 设备命名与映射(逻辑名→物理名);
    2. 设备保护(权限控制);
    3. 设备分配与释放;
    4. 缓冲管理;
    5. 统一的 I/O 接口(read/write)。

代码案例:设备无关层模拟(C++98)

cpp 复制代码
#include <iostream>
#include <string>
#include <map>
#include <vector>
// 新增atoi所需的头文件
#include <cstdlib>
using namespace std;

// 兼容C++98的整数转字符串函数(替代C++11的to_string)
string intToString(int num) {
    if (num == 0) {
        return "0";
    }
    bool isNegative = false;
    string result;
    // 处理负数
    if (num < 0) {
        isNegative = true;
        num = -num;
    }
    // 逐位转换
    while (num > 0) {
        result = char('0' + (num % 10)) + result;
        num /= 10;
    }
    // 补回负号
    if (isNegative) {
        result = "-" + result;
    }
    return result;
}

// 前置声明:设备驱动基类(统一接口)
class DeviceDriver {
public:
    virtual string read(const string& param) = 0;
    virtual bool write(const string& param, const string& data) = 0;
    virtual string getDeviceType() = 0;
    virtual ~DeviceDriver() {}
};

// 磁盘驱动实现
class DiskDriver : public DeviceDriver {
public:
    string read(const string& param) {
        // atoi需要<cstdlib>头文件,已添加
        int blockNum = atoi(param.c_str());
        // 用自定义的intToString替代to_string,兼容C++98
        return "磁盘读块" + param + ":数据" + intToString(blockNum * 100);
    }

    bool write(const string& param, const string& data) {
        cout << "磁盘写块" << param << ":" << data << endl;
        return true;
    }

    string getDeviceType() {
        return "DISK";
    }
};

// 键盘驱动实现
class KeyboardDriver : public DeviceDriver {
public:
    string read(const string& param) {
        return "键盘输入:用户按下" + param;
    }

    bool write(const string& param, const string& data) {
        cout << "键盘无写操作,忽略" << endl;
        return false;
    }

    string getDeviceType() {
        return "KEYBOARD";
    }
};

// 与设备无关的I/O软件层
class DeviceIndependentLayer {
private:
    // 逻辑设备名 → 物理驱动映射
    map<string, DeviceDriver*> deviceMap;
    // 设备分配状态:逻辑名 → 是否被占用
    map<string, bool> deviceAllocated;

public:
    // 注册设备(逻辑名+驱动)
    void registerDevice(const string& logicalName, DeviceDriver* driver) {
        deviceMap[logicalName] = driver;
        deviceAllocated[logicalName] = false;
        cout << "注册设备:" << logicalName << " → " << driver->getDeviceType() << endl;
    }

    // 分配设备(避免冲突)
    bool allocateDevice(const string& logicalName) {
        if (deviceMap.find(logicalName) == deviceMap.end()) {
            cout << "设备不存在:" << logicalName << endl;
            return false;
        }
        if (deviceAllocated[logicalName]) {
            cout << "设备已被占用:" << logicalName << endl;
            return false;
        }
        deviceAllocated[logicalName] = true;
        cout << "分配设备成功:" << logicalName << endl;
        return true;
    }

    // 释放设备
    void releaseDevice(const string& logicalName) {
        if (deviceMap.find(logicalName) != deviceMap.end()) {
            deviceAllocated[logicalName] = false;
            cout << "释放设备:" << logicalName << endl;
        }
    }

    // 统一读接口(设备无关)
    string read(const string& logicalName, const string& param) {
        if (!allocateDevice(logicalName)) {
            return "读失败:设备分配失败";
        }
        string result = deviceMap[logicalName]->read(param);
        releaseDevice(logicalName);
        return result;
    }

    // 统一写接口(设备无关)
    bool write(const string& logicalName, const string& param, const string& data) {
        if (!allocateDevice(logicalName)) {
            return false;
        }
        bool result = deviceMap[logicalName]->write(param, data);
        releaseDevice(logicalName);
        return result;
    }
};

int main() {
    DeviceIndependentLayer ioLayer;
    // 注册设备(逻辑名:硬盘、键盘;物理驱动:DiskDriver、KeyboardDriver)
    ioLayer.registerDevice("硬盘", new DiskDriver());
    ioLayer.registerDevice("键盘", new KeyboardDriver());

    // 统一接口读设备(无需关心底层驱动)
    cout << ioLayer.read("硬盘", "1") << endl;
    cout << ioLayer.read("键盘", "Enter键") << endl;

    // 统一接口写设备
    ioLayer.write("硬盘", "2", "测试数据");
    ioLayer.write("键盘", "", "无效数据");

    return 0;
}
代码说明:
  1. 采用多态实现设备驱动的统一接口,体现 "设备无关" 核心思想;
  2. 实现设备注册、分配、释放的完整逻辑,避免设备冲突;
  3. 应用程序只需调用统一的 read/write 接口,无需关心底层硬件差异;
  4. 编译命令:g++ -std=c++98 device_independent.cpp -o device_independent && ./device_independent

6.6 用户层的 I/O 软件

核心概念

用户层 I/O 软件是应用程序直接接触的 I/O 层,主要包括:

  1. 库函数:如 C 语言的 fopen/fread/fwrite(封装系统调用);
  2. SPOOLing 技术(假脱机):将独占设备(如打印机)模拟为共享设备;
  3. 用户态 I/O 工具:如图形界面的文件管理器。

核心案例:SPOOLing 技术模拟(C++98)

复制代码
#include <iostream>
#include <string>
#include <queue>
#include <vector>
using namespace std;

// 模拟打印任务
struct PrintTask {
    string taskId;
    string content;
    string userName;

    PrintTask(const string& id, const string& cnt, const string& name)
        : taskId(id), content(cnt), userName(name) {}
};

// SPOOLing管理器(假脱机)
class SPOOLingManager {
private:
    queue<PrintTask> taskQueue; // 打印任务队列(内存缓冲区)
    bool isPrinterBusy; // 打印机是否忙
    // 模拟磁盘输出井(存储待打印任务)
    vector<PrintTask> outputPool;

public:
    SPOOLingManager() : isPrinterBusy(false) {}

    // 用户提交打印任务(用户层)
    void submitTask(const PrintTask& task) {
        cout << "用户[" << task.userName << "]提交打印任务:" << task.taskId << endl;
        // 1. 先写入输出井(磁盘)
        outputPool.push_back(task);
        // 2. 将任务加入内存队列
        taskQueue.push(task);
        // 3. 尝试调度打印
        schedulePrint();
    }

    // 调度打印(后台进程)
    void schedulePrint() {
        if (isPrinterBusy || taskQueue.empty()) {
            return;
        }
        isPrinterBusy = true;
        // 取出队列头任务
        PrintTask task = taskQueue.front();
        taskQueue.pop();
        // 模拟打印过程
        cout << "开始打印任务:" << task.taskId << endl;
        cout << "打印内容:" << task.content << endl;
        // 打印完成
        cout << "任务" << task.taskId << "打印完成" << endl;
        isPrinterBusy = false;
        // 继续调度下一个任务
        schedulePrint();
    }

    // 查看待打印任务
    void showPendingTasks() {
        cout << "待打印任务数:" << taskQueue.size() << endl;
        cout << "输出井任务数:" << outputPool.size() << endl;
    }
};

int main() {
    SPOOLingManager spooling;

    // 多个用户提交打印任务(独占设备模拟为共享)
    spooling.submitTask(PrintTask("T001", "操作系统笔记", "张三"));
    spooling.submitTask(PrintTask("T002", "C++编程教程", "李四"));
    spooling.submitTask(PrintTask("T003", "数据结构作业", "王五"));

    // 查看待打印任务
    spooling.showPendingTasks();

    return 0;
}
代码说明:
  1. 模拟 SPOOLing 核心逻辑:用户任务先写入磁盘输出井,再由后台进程调度打印;
  2. 实现独占设备(打印机)的共享使用,体现用户层 I/O 软件的核心价值;
  3. 编译命令:g++ -std=c++98 spooling.cpp -o spooling && ./spooling

6.7 缓冲区管理

核心概念

缓冲区是内存中专门用于缓解 CPU 与外设速度差异的区域,常见类型:

  1. 单缓冲:一个缓冲区,CPU 与外设交替使用;
  2. 双缓冲:两个缓冲区,CPU 与外设可并行操作;
  3. 循环缓冲:多个缓冲区组成环形队列,适用于大批量数据传输;
  4. 缓冲池:系统级的缓冲区管理,统一分配 / 回收缓冲区。

架构图(双缓冲)

代码案例:循环缓冲区实现(C++98)

复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 循环缓冲区
template <typename T, int SIZE>
class CircularBuffer {
private:
    vector<T> buffer; // 缓冲区数组
    int head; // 读指针(取数据)
    int tail; // 写指针(存数据)
    int count; // 已存储数据量

public:
    CircularBuffer() : buffer(SIZE), head(0), tail(0), count(0) {}

    // 写入数据(生产者)
    bool write(const T& data) {
        if (isFull()) {
            cout << "缓冲区满,写入失败" << endl;
            return false;
        }
        buffer[tail] = data;
        tail = (tail + 1) % SIZE; // 循环移动写指针
        count++;
        cout << "写入数据:" << data << ",当前数据量:" << count << endl;
        return true;
    }

    // 读取数据(消费者)
    bool read(T& data) {
        if (isEmpty()) {
            cout << "缓冲区空,读取失败" << endl;
            return false;
        }
        data = buffer[head];
        head = (head + 1) % SIZE; // 循环移动读指针
        count--;
        cout << "读取数据:" << data << ",当前数据量:" << count << endl;
        return true;
    }

    // 判断缓冲区是否满
    bool isFull() {
        return count == SIZE;
    }

    // 判断缓冲区是否空
    bool isEmpty() {
        return count == 0;
    }

    // 获取缓冲区大小
    int size() {
        return SIZE;
    }

    // 获取已使用大小
    int usedSize() {
        return count;
    }
};

int main() {
    // 创建大小为5的循环缓冲区
    CircularBuffer<string, 5> cb;

    // 模拟生产者写入数据
    cb.write("数据1");
    cb.write("数据2");
    cb.write("数据3");
    cb.write("数据4");
    cb.write("数据5");
    cb.write("数据6"); // 缓冲区满,写入失败

    // 模拟消费者读取数据
    string data;
    cb.read(data);
    cb.read(data);

    // 再次写入
    cb.write("数据6");

    return 0;
}
代码说明:
  1. 基于模板实现通用循环缓冲区,支持任意数据类型;
  2. 实现核心操作:写入、读取、判空 / 判满,符合循环缓冲的核心逻辑;
  3. 模拟生产者 - 消费者模型,体现缓冲区缓解速度差异的作用;
  4. 编译命令:g++ -std=c++98 circular_buffer.cpp -o circular_buffer && ./circular_buffer

6.8 磁盘存储器的性能和调度

核心概念

1. 磁盘性能指标
  • 寻道时间:磁头移动到目标磁道的时间(影响最大);
  • 旋转延迟:磁盘旋转到目标扇区的时间;
  • 传输时间:数据读写的时间;
  • 总时间 = 寻道时间 + 旋转延迟 + 传输时间。
2. 磁盘调度算法(优化寻道时间)
算法 核心逻辑 优点 缺点
FCFS(先来先服务) 按请求顺序处理 公平、简单 寻道时间长,效率低
SSTF(最短寻道优先) 优先处理离当前磁头最近的请求 寻道时间短 可能饥饿
SCAN(电梯算法) 磁头单向移动,处理完所有请求再反向 无饥饿,效率高 响应不均匀
CSCAN(循环电梯) 磁头到最远端后,直接返回起点(不反向处理) 响应更均匀 寻道时间略长于 SCAN

代码案例:磁盘调度算法实现(C++98)

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <string>
// 新增头文件:INT_MAX定义在<cstdlib>中
#include <cstdlib>
using namespace std;

// 磁盘调度器
class DiskScheduler {
private:
    int currentTrack; // 当前磁头位置
    vector<int> requests; // 磁道请求队列

    // 计算总寻道长度(替换C++11范围for为C++98索引循环)
    int calculateSeekLength(const vector<int>& order) {
        int total = 0;
        int pos = currentTrack;
        // C++98兼容写法:基于索引遍历vector
        for (int i = 0; i < order.size(); ++i) {
            int track = order[i];
            total += abs(track - pos);
            pos = track;
        }
        return total;
    }

public:
    DiskScheduler(int initTrack, const vector<int>& req)
        : currentTrack(initTrack), requests(req) {}

    // 1. FCFS调度
    void FCFS() {
        vector<int> order = requests;
        int total = calculateSeekLength(order);
        cout << "===== FCFS 调度 =====" << endl;
        cout << "调度顺序:";
        // C++98兼容写法
        for (int i = 0; i < order.size(); ++i) {
            int track = order[i];
            cout << track << " ";
        }
        cout << endl << "总寻道长度:" << total << endl;
    }

    // 2. SSTF调度
    void SSTF() {
        vector<int> remaining = requests;
        vector<int> order;
        int pos = currentTrack;

        while (!remaining.empty()) {
            // 找到离当前位置最近的磁道
            int minDist = INT_MAX;
            int idx = -1;
            for (int i = 0; i < remaining.size(); ++i) {
                int dist = abs(remaining[i] - pos);
                if (dist < minDist) {
                    minDist = dist;
                    idx = i;
                }
            }
            // 将该磁道加入调度顺序
            order.push_back(remaining[idx]);
            pos = remaining[idx];
            // 从剩余队列移除
            remaining.erase(remaining.begin() + idx);
        }

        int total = calculateSeekLength(order);
        cout << "===== SSTF 调度 =====" << endl;
        cout << "调度顺序:";
        // C++98兼容写法
        for (int i = 0; i < order.size(); ++i) {
            int track = order[i];
            cout << track << " ";
        }
        cout << endl << "总寻道长度:" << total << endl;
    }

    // 3. SCAN调度(电梯算法,默认向磁道号增大方向)
    void SCAN(int maxTrack) {
        vector<int> left, right;
        // 分离当前磁头左侧和右侧的请求(替换范围for)
        for (int i = 0; i < requests.size(); ++i) {
            int track = requests[i];
            if (track < currentTrack) {
                left.push_back(track);
            } else {
                right.push_back(track);
            }
        }
        // 右侧升序,左侧降序(电梯单向移动)
        sort(right.begin(), right.end());
        sort(left.begin(), left.end(), greater<int>());

        // 调度顺序:先右后左
        vector<int> order = right;
        order.push_back(maxTrack); // 到最远端
        order.insert(order.end(), left.begin(), left.end());

        int total = calculateSeekLength(order);
        cout << "===== SCAN 调度 =====" << endl;
        cout << "调度顺序:";
        // C++98兼容写法
        for (int i = 0; i < order.size(); ++i) {
            int track = order[i];
            if (i > 0) cout << " "; // 优化输出格式,避免开头空格
            cout << track;
        }
        cout << endl << "总寻道长度:" << total << endl;
    }
};

int main() {
    // 测试用例:初始磁头位置50,磁道请求[17, 79, 90, 100, 10, 48, 55],最大磁道100
    vector<int> requests;
    requests.push_back(17);
    requests.push_back(79);
    requests.push_back(90);
    requests.push_back(100);
    requests.push_back(10);
    requests.push_back(48);
    requests.push_back(55);

    DiskScheduler ds(50, requests);
    ds.FCFS();
    ds.SSTF();
    ds.SCAN(100);

    return 0;
}
代码说明:
  1. 实现 FCFS、SSTF、SCAN 三种核心磁盘调度算法;
  2. 计算总寻道长度,直观对比不同算法的效率;
  3. 代码结构清晰,注释完整,便于理解调度算法的核心逻辑;
  4. 编译命令:g++ -std=c++98 disk_scheduler.cpp -o disk_scheduler && ./disk_scheduler

习题(核心考点 + 代码实操)

一、选择题

  1. 以下哪种 I/O 控制模型效率最高()
    1. A. 程序 I/O
    2. B. 中断驱动
    3. C. DMA
    4. D. 通道
  2. 设备驱动程序的核心作用是()
    1. A. 屏蔽设备差异
    2. B. 转换 I/O 指令
    3. C. 管理缓冲区
    4. D. 分配设备
  3. 磁盘调度中,能避免饥饿的算法是()
    1. A. FCFS
    2. B. SSTF
    3. C. SCAN
    4. D. 以上都不是

二、编程题

实现缓冲池(Buffer Pool),要求:

  1. 支持缓冲区的申请(alloc)和释放(free);
  2. 区分空缓冲区、满缓冲区、工作缓冲区;
  3. 模拟生产者写入数据、消费者读取数据。

习题答案

选择题:1.D 2.B 3.C
编程题参考代码(C++98):
复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 缓冲区状态
enum BufferState { FREE, FULL, WORKING };

// 缓冲区节点
struct BufferNode {
    string data;
    BufferState state;
    int id;

    BufferNode(int i) : id(i), state(FREE) {}
};

// 缓冲池
class BufferPool {
private:
    vector<BufferNode> pool; // 缓冲区数组
    int poolSize; // 缓冲池大小

public:
    BufferPool(int size) : poolSize(size) {
        // 初始化缓冲池
        for (int i = 0; i < size; ++i) {
            pool.push_back(BufferNode(i));
        }
    }

    // 申请空缓冲区(生产者用)
    BufferNode* allocFreeBuffer() {
        for (int i = 0; i < poolSize; ++i) {
            if (pool[i].state == FREE) {
                pool[i].state = WORKING;
                cout << "申请空缓冲区:ID=" << pool[i].id << endl;
                return &pool[i];
            }
        }
        cout << "无可用空缓冲区" << endl;
        return NULL;
    }

    // 申请满缓冲区(消费者用)
    BufferNode* allocFullBuffer() {
        for (int i = 0; i < poolSize; ++i) {
            if (pool[i].state == FULL) {
                pool[i].state = WORKING;
                cout << "申请满缓冲区:ID=" << pool[i].id << endl;
                return &pool[i];
            }
        }
        cout << "无可用满缓冲区" << endl;
        return NULL;
    }

    // 释放缓冲区(设为满)
    void freeToFull(BufferNode* node) {
        if (node) {
            node->state = FULL;
            cout << "释放缓冲区为满:ID=" << node->id << endl;
        }
    }

    // 释放缓冲区(设为空)
    void freeToFree(BufferNode* node) {
        if (node) {
            node->state = FREE;
            node->data = "";
            cout << "释放缓冲区为空:ID=" << node->id << endl;
        }
    }

    // 生产者写入数据
    bool produce(const string& data) {
        BufferNode* node = allocFreeBuffer();
        if (!node) return false;
        node->data = data;
        freeToFull(node);
        cout << "生产者写入数据:" << data << "(缓冲区ID=" << node->id << ")" << endl;
        return true;
    }

    // 消费者读取数据
    bool consume(string& data) {
        BufferNode* node = allocFullBuffer();
        if (!node) return false;
        data = node->data;
        freeToFree(node);
        cout << "消费者读取数据:" << data << "(缓冲区ID=" << node->id << ")" << endl;
        return true;
    }
};

int main() {
    BufferPool pool(3); // 缓冲池大小为3

    // 生产者写入数据
    pool.produce("数据1");
    pool.produce("数据2");
    pool.produce("数据3");
    pool.produce("数据4"); // 缓冲池满,写入失败

    // 消费者读取数据
    string data;
    pool.consume(data);
    pool.consume(data);

    // 再次写入
    pool.produce("数据4");

    return 0;
}

编译命令:g++ -std=c++98 buffer_pool.cpp -o buffer_pool && ./buffer_pool

总结

核心知识点回顾

  1. I/O 系统的核心是高效、统一地完成 CPU 与外设的数据交互,核心模型包括中断驱动、DMA 等;
  2. 设备驱动程序是硬件与操作系统的 "翻译官",而设备无关层屏蔽了设备差异,实现 "一次编写,多设备兼容";
  3. 缓冲区(单缓冲、双缓冲、循环缓冲)缓解 CPU 与外设的速度差异,磁盘调度算法(SCAN/SSTF)优化寻道时间,是提升 I/O 效率的核心手段。

实操关键点

  1. 所有代码均基于 C++98 标准编写,可直接编译运行;
  2. 核心案例(中断处理、设备驱动、磁盘调度)覆盖本章高频考点,建议动手调试理解逻辑;
  3. 架构图 / 流程图的 Mermaid 代码可直接复制到 Mermaid 编辑器生成高清图,提示词可用于 AI 工具生成更美观的架构图。

如果本文对你有帮助,欢迎点赞、收藏、关注!有任何问题,评论区交流~

相关推荐
霍理迪2 小时前
JS对象与函数初相识
开发语言·javascript·ecmascript
线束线缆组件品替网2 小时前
Stewart Connector RJ45 以太网线缆高速接口设计解析
服务器·网络·人工智能·音视频·硬件工程·材料工程
风筝在晴天搁浅2 小时前
hot100 104.二叉树的最大深度
java·算法
潇冉沐晴2 小时前
div3 970个人笔记
c++·笔记·算法
晔子yy2 小时前
说一下Java的垃圾回收机制
java·开发语言
王老师青少年编程2 小时前
2023年12月GESP真题及题解(C++八级): 奖品分配
c++·题解·真题·gesp·csp·八级·奖品分配
superman超哥2 小时前
Rust 与数据库连接池的集成:从理论到生产实践
开发语言·rust·编程语言·rust与数据库连接池的集成
tqs_123452 小时前
@transactional事务失效场景
java·数据库·mybatis
AI即插即用2 小时前
即插即用系列 | AAAI 2025 Mesorch:CNN与Transformer的双剑合璧:基于频域增强与自适应剪枝的篡改定位
人工智能·深度学习·神经网络·计算机视觉·cnn·transformer·剪枝