磁盘调度算法(C++及文档)

一、实验目的

通过这次实验,加深对磁盘调度算法的理解,进一步掌握先来先服务FCFS,最短寻道时间优先SSTF,SCAN和循环SCAN算法的实现方法。

二、需求分析

2.1 程序设计任务

本程序旨在模拟磁盘调度算法,包括以下四种算法:

  • 先来先服务调度算法(FCFS);
  • 最短寻道时间优先调度算法(SSTF);
  • 扫描算法(SCAN);
  • 循环扫描算法(C-SCAN)。

程序能够按照用户输入的参数,依次执行上述调度算法,计算并输出每种算法的磁头移动路径、总寻道距离和平均寻道长度。

2.2 输入内容、形式范围

1.磁道数量 (n):

输入内容:正整数 n,表示磁盘上磁道访问序列的数量。

2.磁道访问序列:

输入内容:磁道号组成的正整数序列。

3.起始磁道号 (m):

输入内容:程序开始运行时的磁头所在磁道号。

4.磁头移动方向(仅适用于 SCAN 和 C-SCAN):

输入内容:用户选择磁头移动方向(1 表示磁道号增加的方向,0 表示磁道号减少的方向)。

5.调度算法选择:

输入内容:整数值,用于选择调度算法:

1 - FCFS

2 - SSTF

3 - SCAN

4 - C-SCAN。

2.3 输出形式

算法过程展示:

程序根据所选调度算法,输出磁头访问顺序和每次移动的寻道距离。

统计信息:

磁头移动的总寻道距离;

平均寻道长度。

优化对比:

用户可以运行多个调度算法或输入多种磁道序列,比较它们的性能(总寻道距离和平均寻道长度)。

2.4 测试数据

2.4.1 正确的输入

2.4.2 错误的输入

算法选择不在1-5间

三、概要设计

3.1 抽象数据类型的定义

1.磁盘调度数据类型

用于表示磁盘调度算法中的核心属性和辅助变量,包括:

核心数据结构和属性:

  • vector<int> cidao:磁道访问序列,用于记录请求访问的磁道号。
  • int currentTrack:磁头当前的位置(开始磁道号)。
  • int sum:记录磁头移动的总距离,即磁头从一个磁道移动到另一个磁道所经过的总磁道数。
  • float averageDistance:平均寻道长度(计算公式为 sum / 磁道数量)。
  • int direction:磁头移动方向,仅适用于 SCAN 和 C-SCAN 算法(1 表示增大方向,0 表示减小方向)。

辅助变量:

  • vector<int> sortedCidao:用于存储排序后的磁道号序列(适用于 SSTF、SCAN 和 C-SCAN 算法的调度需求)。
  • int l 和 int r:左指针和右指针,分别表示当前磁道在排序后序列中的左右邻居(SSTF 算法中使用)。
  • int tempTrack:临时变量,用于保存当前磁头位置,便于更新。

2.辅助变量描述

  • sum:记录磁头移动总距离,供每种算法统计每次磁头移动距离之和,用于最终计算。
  • averageDistance:每种算法计算完毕后自动求得的平均寻道长度。
  • direction:在 SCAN 和 C-SCAN 算法中,辅助决定磁头是否反向处理边界条件或者回绕。
  • k:索引变量,用于记录磁头访问序列排序中第一个大于等于 currentTrack 的元素(在 SCAN 和 C-SCAN 中用于判断方向)。
  • vector<int>::iterator:用于遍历磁道序列。

3.2 主程序流程

1.输入阶段

输入磁道数量、访问序列、磁头起始位置、方向(用于 SCAN 和 C-SCAN),以及选择的算法类型(1. FCFS, 2. SSTF, 3. SCAN, 4. C-SCAN)。

校验数据合法性(非负整数、范围正确、方向为 0 或 1)。

2.算法处理阶段

  • FCFS:按输入顺序访问磁道,累计移动距离。
  • SSTF:每次优先访问最近的磁道,重复直到访问完所有磁道。
  • SCAN:磁头按方向扫描到边界后反向,累计移动距离。
  • C-SCAN:磁头按方向扫描到边界后回绕到另一端继续扫描。

3.结果输出阶段

输出每种算法的访问顺序和总移动距离。

计算并输出平均寻道长度 averageDistance。

四、详细设计

4.1 磁盘调度算法实现

1.输入阶段

用户输入:

磁道数量 n,即磁盘调度中要访问的磁道数量;

磁道访问序列,存入 cidao 数组;

当前磁头起始位置,存入 currentTrack;

对于 SCAN 和 C-SCAN 的算法,还需输入磁头移动的方向(1 表示向正方向,即磁道号增大的方向,0 表示向负方向,即磁道号减小的方向);

用户选择的调度算法类型:1 表示 FCFS,2 表示 SSTF,3 表示 SCAN,4 表示 C-SCAN。

数据校验:

确保磁道访问序列是非负整数;

确保磁头起始位置 currentTrack 在磁道访问序列的合理范围内;

确保输入的方向符合要求(SCAN 和 C-SCAN 中的方向需为 0 或 1)。

2.算法处理阶段

FCFS(先来先服务)实现:

从 cidao 队列按顺序访问磁道号。

计算两相邻磁道号之间的绝对差值,并累积到 sum 中记录总移动距离。

SSTF(最短寻道时间优先)实现:

将磁道访问序列cidao排序成sortedCidao。

找到 currentTrack 在 sortedCidao 中的位置 k,然后比较左右两侧(索引 l = k-1 和 r = k)的距离,从距离最近的开始访问并更新磁头当前位置。

重复上述过程,直到访问完所有磁道号。

SCAN 实现:

将磁道访问序列 cidao 排序生成 sortedCidao。

根据磁头的移动方向(direction = 1 表示递增,direction = 0 表示递减):

  • 递增方向:从当前磁头位置 currentTrack 向右访问比它大的磁道号依次处理。直到达到最大磁道号后切换方向(如果到达边界则旋转回边界磁道)。
  • 递减方向:从当前磁头位置向左访问比它小的磁道号。直到达到最小磁道号后切换方向。

在每次移动操作中,记录移动距离,并累积到 sum 中。

C-SCAN(循环扫描)实现:

将磁道访问序列 cidao 排序生成 sortedCidao。

根据磁头的移动方向固定为循环扫描逻辑:

  • 递增方向:从 currentTrack 开始向右扫描比它大的磁道号,访问到达最大磁道号后直接回绕到最小磁道号,然后继续从小到大访问。
  • 递减方向:从 currentTrack 开始向左扫描比它小的磁道号,访问到达最小磁道号后直接回绕到最大磁道号,然后继续从大到小访问。

在每次移动操作中,记录磁头移动距离,并累积到 sum 中。

五、调试分析

5.1 调试过程中遇到的问题及解决方案

问题 1: SCAN算法找到第一个位置后不知如何用迭代器反向遍历

解决方案: 使用rend()和make_reverse_iterator()

5.2 算法的时空复杂度分析

5.2.1 算法时间复杂度分析

(1) FCFS(先来先服务)

主要操作是计算磁道之间的绝对差值,进行一次遍历即可完成。

时间复杂度:O(m),其中 m 为磁道数量。

(2) SSTF(最短寻道时间优先)

需要对磁道序列进行排序,时间复杂度为 O(m log m)。

在排序后的数组中,每次选择距离当前磁道最近的磁道(线性搜索),时间复杂度为 O(m)。

总时间复杂度:O(m log m)。

(3) SCAN(扫描算法)

需要对磁道序列进行排序,时间复杂度为 O(m log m)。

根据磁头的移动方向,依次遍历磁道,时间复杂度为 O(m)。

总时间复杂度:O(m log m)。

(4) C-SCAN(循环扫描算法)

同样需要对磁道序列进行排序,时间复杂度为 O(m log m)。

遍历磁道两次(顺序扫描一次,回绕后再扫描一次),时间复杂度为 O(m)。

总时间复杂度:O(m log m)。

5.2.2 空间复杂度分析

(1) FCFS

仅需存储磁道序列(O(m))和一些辅助变量(O(1))。

空间复杂度:O(m)。

(2) SSTF

需要存储排序后的磁道序列(O(m))以及一些辅助变量(O(1))。

空间复杂度:O(m)。

(3) SCAN

需要存储排序后的磁道序列(O(m))以及辅助变量(O(1))。

空间复杂度:O(m)。

(4) C-SCAN

同样需要存储排序后的磁道序列(O(m))和辅助变量(O(1))。

空间复杂度:O(m)。

5.2.3 改进设想

减少磁道排序的负担:对磁道序列使用更高效的排序算法,或者仅排序当前位置附近的子序列,减少排序开销。

并行化处理:针对排序操作,可以采用多线程技术并行实现,从而提高效率。

缓存访问结果:通过缓存访问过的磁道序列,避免重复计算移动距离,从而降低时间复杂度。

5.3 经验和体会

通过本次项目,深入理解了磁盘调度算法的原理及实现方法,尤其是在处理数据校验和优化算法效率方面积累了经验。

  • FCFS 和 SSTF 算法实现相对简单,但需要注意边界条件处理。
  • SCAN 和 C-SCAN 算法更复杂,但提供更合理的寻道性能,在实现过程中需要严格遵循磁头移动方向和边界条件的处理逻辑。

通过对比不同算法,深刻体会到算法选择的合理性对系统性能的影响,同时也了解到在实现过程中注重代码可读性和效率的重要性。

六、用户使用说明

1.输入参数

程序会提示用户输入磁道数量、磁道序列、磁头当前位置,以及磁头移动方向(仅适用于 SCAN 和 C-SCAN 算法)。

2.选择算法

用户可根据提示选择调度算法(1-FCFS, 2-SSTF, 3-SCAN, 4-C-SCAN)。

输入 5 退出程序。

3.查看结果

程序会输出磁道访问顺序、总移动距离,以及平均寻道长度。

七、测试结果

1.输入:

2.先来先服务算法(FCFS):

3.最短服务时间优先算法(SSTF)

4.扫描算法(SCAN)

5.循环扫描算法(C-SCAN)

#include <bits/stdc++.h>
using namespace std;

// 先来先服务调度算法 FCFS
void FCFS(const vector<int>& array, int m) {
    int sum = 0;
    float avg;
    int now;

    cout << "\n请输入当前的磁道号: ";
    cin >> now;
    cout << "\nFCFS 调度结果: ";

    for (int i = 0; i < m; i++)
        cout << array[i] << " ";

    sum = abs(now - array[0]);
    for (int i = 1; i < m; i++)
        sum += abs(array[i] - array[i - 1]); // 移动的总道数

    avg = static_cast<float>(sum) / m; // 计算平均寻道长度
    cout << "\n移动的总道数: " << sum << "\n";
    cout << "平均寻道长度: " << avg << "\n";
}

// 最短服务时间优先调度算法 SSTF
void SSTF(vector<int> array, int m) {
    sort(array.begin(), array.end());

    for (int i = 0; i < m; i++)
        cout << array[i] << " ";

    int now, sum = 0;
    cout << "\n请输入当前的磁道号: ";
    cin >> now;
    cout << "\nSSTF 调度结果: ";

    // 找到当前磁道位置右邻位置
    int k = 0;
    while (k < m && array[k] < now)
        k++;

    int l = k - 1;
    int r = k;

    while (l >= 0 || r < m) {
        if (l >= 0 && (r == m || (abs(now - array[l]) <= abs(array[r] - now)))) {
            sum += abs(now - array[l]);
            now = array[l--];
            cout << now << " ";
        }
        else if (r < m) {
            sum += abs(array[r] - now);
            now = array[r++];
            cout << now << " ";
        }
    }

    float avg = static_cast<float>(sum) / m;
    cout << "\n移动的总道数: " << sum << "\n";
    cout << "平均寻道长度: " << avg << "\n";
}

// 扫描算法 SCAN
void SCAN(vector<int> array, int m) {
    sort(array.begin(), array.end()); // 使用 std::sort 进行排序

    for (int i = 0; i < m; i++)
        cout << array[i] << " ";

    int now, direction;
    cout << "\n请输入当前的磁道号: ";
    cin >> now;
    cout << "请输入磁头移动方向 (1代表正向,0代表反向): ";
    cin >> direction;

    cout << "\nSCAN 调度结果:";

    int sum = 0;

    if (direction == 1) { // 正向扫描
        auto it = lower_bound(array.begin(), array.end(), now);
        for (auto i = it; i != array.end(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }
        for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }
    }
    else { // 反向扫描
        auto it = lower_bound(array.begin(), array.end(), now);
        for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }
        for (auto i = it; i != array.end(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }
    }

    float avg = static_cast<float>(sum) / m;
    cout << "\n移动的总道数: " << sum << "\n";
    cout << "平均寻道长度: " << avg << "\n";
}

// 循环扫描算法 C-SCAN
void CSCAN(vector<int> array, int m) {
    sort(array.begin(), array.end());

    for (int i = 0; i < m; i++)
        cout << array[i] << " ";

    int now, direction;
    cout << "\n请输入当前的磁道号: ";
    cin >> now;
    cout << "请输入磁头移动方向 (1代表正向,0代表反向): ";
    cin >> direction;

    cout << "\nC-SCAN 调度结果:";
    int sum = 0;

    // 定位开始位置
    auto it = lower_bound(array.begin(), array.end(), now);

    if (direction == 1) { // 正向移动
        // 扫描到磁道号最大的地方
        for (auto i = it; i != array.end(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }

        // 回绕到磁道号最小的地方
        if (!array.empty()) {
            sum += abs(now - array[0]);
            now = array[0];
            for (auto i = array.begin(); i != it; i++) {
                cout << *i << " ";
                sum += abs(now - *i);
                now = *i;
            }
        }
    }
    else { // 反向移动
        // 从当前磁道号开始反向扫描
        for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
            cout << *i << " ";
            sum += abs(now - *i);
            now = *i;
        }

        // 回绕到磁道号最大的地方
        if (!array.empty()) {
            sum += abs(now - array.back());
            now = array.back();
            for (auto i = array.end(); i != it; i++) {
                cout << *i << " ";
                sum += abs(now - *i);
                now = *i;
            }
        }
    }

    float avg = static_cast<float>(sum) / m;
    cout << "\n移动的总道数: " << sum << "\n";
    cout << "平均寻道长度: " << avg << "\n";
}


// 主函数
int main() {
    vector<int> cidao; // 磁道号序列
    int count;


    cout << "请先输入磁道数量: ";
    cin >> count;

    cidao.resize(count);
    cout << "请先输入磁道序列: \n";
    for (int i = 0; i < count; i++)
        cin >> cidao[i];

    cout << "\n磁道读取结果: \n";
    for (int i = 0; i < count; i++)
        cout << cidao[i] << " ";
    cout << "\n";

    while (true) {
        int choice;
        cout << "\n算法选择: \n";
        cout << " 1. 先来先服务算法(FCFS) \n";
        cout << " 2. 最短服务时间优先算法(SSTF) \n";
        cout << " 3. 扫描算法(SCAN) \n";
        cout << " 4. 循环扫描算法(C-SCAN) \n";
        cout << " 5. 退出\n";
        cout << "\n请选择: ";
        cin >> choice;

        if (choice == 5)
            break;

        switch (choice) {
        case 1:
            FCFS(cidao, count);
            break;
        case 2:
            SSTF(cidao, count);
            break;
        case 3:
            SCAN(cidao, count);
            break;
        case 4:
            CSCAN(cidao, count);
            break;
        default:
            cout << "无效输入,请重新选择!\n";
        }
    }

    return 0;
}

这个错误信息通常出现在Windows编程环境中,特别是在使用Win32 API或MFC进行界面开发时。这个问题的核心在于线程模型的使用不当。让我来详细解释一下: 1. 错误含义: "系统错误,调动线程必须为STA"意味着程序试图从一个非单线程单元(STA)线程中操作UI组件,而Windows要求所有UI操作必须在STA线程中进行。 2. 原因分析: 许多UI组件(如窗口、控件等)需要在STA线程中创建和操作。如果在MTA(多线程单元)或其他非STA线程中操作这些组件,就会导致这个错误。 3. 解决方法: a. 确保主线程是STA的: 在你的应用程序入口点(如WinMain或main函数)中,使用适当的线程模型初始化。例如,在Win32应用程序中,确保在调用任何UI函数之前调用了CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)。 b. 使用消息队列: 确保你的STA线程有一个消息队列。对于UI线程,通常可以通过调用GetMessage、PeekMessage等函数来创建消息队列。 c. 使用适当的线程: 所有UI操作都应该在主UI线程中进行。如果你在其他线程中需要更新UI,应该使用线程间通信机制(如PostMessage或SendMessage)将更新请求发送到主UI线程。 d. 同步对象访问: 如果多个线程需要访问共享对象,确保使用适当的同步机制(如临界区、互斥锁等)来保护这些对象。 4. 示例代码: ```cpp // 在WinMain中 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { // 处理初始化失败的情况 } // 主消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 在其他线程中更新UI PostMessage(hWnd, WM_USER_UPDATE_UI, 0, 0); ``` 通过遵循这些步骤,你可以确保所有UI操作都在正确的线程上下文中执行,从而避免这个系统错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值