69天探索操作系统-第48天:构建进程调度模拟器

180 阅读8分钟
pro18.avif

1. 介绍

进程调度是操作系统的基本方面,负责确定哪些进程可以访问CPU以及何时访问。调度器的目标是最大化CPU利用率,确保公平性,并最小化进程的等待时间和周转时间。本文提供的代码实现了一个进程调度模拟器,允许实验不同的调度算法,如先来先服务(FCFS)、最短作业优先(SJF)、优先级调度和轮转(RR)。

模拟器使用进程结构来表示每个进程,其中包括pid、优先级、burst_timearrival_time和状态等属性。调度器结构管理进程列表并跟踪当前模拟时间。这种设置提供了一个灵活的框架,用于模拟和分析不同调度算法在各种工作负载下的行为。

2.调度器组件

调度器的核心功能通过调度器结构实现,该结构管理进程列表并跟踪模拟的进度。create_scheduler 函数使用指定的容量和时间片初始化调度器。add_process 函数将进程添加到调度器中,而 create_process 函数初始化单个进程及其属性。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

typedef enum {
    READY,
    RUNNING,
    BLOCKED,
    TERMINATED
} ProcessState;

typedef struct {
    int pid;
    int priority;
    int burst_time;
    int remaining_time;
    int arrival_time;
    int waiting_time;
    int turnaround_time;
    ProcessState state;
} Process;

typedef struct {
    Process **processes;
    int capacity;
    int size;
    int current_time;
    int time_quantum;
} Scheduler;

Scheduler* create_scheduler(int capacity, int time_quantum) {
    Scheduler *scheduler = malloc(sizeof(Scheduler));
    scheduler->processes = malloc(sizeof(Process*) * capacity);
    scheduler->capacity = capacity;
    scheduler->size = 0;
    scheduler->current_time = 0;
    scheduler->time_quantum = time_quantum;
    return scheduler;
}

void add_process(Scheduler *scheduler, Process *process) {
    if (scheduler->size < scheduler->capacity) {
        scheduler->processes[scheduler->size++] = process;
    }
}

Process* create_process(int pid, int priority, int burst_time, int arrival_time) {
    Process *process = malloc(sizeof(Process));
    process->pid = pid;
    process->priority = priority;
    process->burst_time = burst_time;
    process->remaining_time = burst_time;
    process->arrival_time = arrival_time;
    process->waiting_time = 0;
    process->turnaround_time = 0;
    process->state = READY;
    return process;
}

create_scheduler 函数为调度器分配内存并初始化其字段。add_process 函数将进程添加到调度器的列表中,而 create_process 函数则使用提供的属性初始化一个新的进程。这些组件构成了调度模拟器的基础。

3.流程管理

进程管理使用ProcessQueue结构进行处理,该结构实现了一个循环队列,用于管理就绪状态的进程。create_queue函数初始化队列,而enqueue_processdequeue_process函数分别用于向队列中添加和移除进程。

typedef struct {
    Process **queue;
    int front;
    int rear;
    int capacity;
    int size;
} ProcessQueue;

ProcessQueue* create_queue(int capacity) {
    ProcessQueue *queue = malloc(sizeof(ProcessQueue));
    queue->queue = malloc(sizeof(Process*) * capacity);
    queue->front = 0;
    queue->rear = -1;
    queue->capacity = capacity;
    queue->size = 0;
    return queue;
}

void enqueue_process(ProcessQueue *queue, Process *process) {
    if (queue->size < queue->capacity) {
        queue->rear = (queue->rear + 1) % queue->capacity;
        queue->queue[queue->rear] = process;
        queue->size++;
    }
}

Process* dequeue_process(ProcessQueue *queue) {
    if (queue->size > 0) {
        Process *process = queue->queue[queue->front];
        queue->front = (queue->front + 1) % queue->capacity;
        queue->size--;
        return process;
    }
    return NULL;
}

ProcessQueue 结构对于管理就绪状态下的进程至关重要,特别是对于 FCFSRound Robin 等算法。enqueue_process 函数将进程添加到队列的尾部,而 dequeue_process 函数则从队列的前端移除进程。这确保了进程以公平和有序的方式进行管理。

4. 调度算法

模拟器支持多种调度算法,包括先来先服务(FCFS)、最短作业优先(SJF)、优先级调度和轮转调度。schedule_process_fcfs函数实现了 FCFS 算法,按照进程到达的顺序进行调度。该函数使用进程队列来管理就绪队列,并在执行过程中更新每个进程的状态。

typedef enum {
    FCFS,
    SJF,
    PRIORITY,
    ROUND_ROBIN
} SchedulingAlgorithm;

void schedule_process_fcfs(Scheduler *scheduler) {
    Process *current = NULL;
    ProcessQueue *ready_queue = create_queue(scheduler->capacity);

    while (true) {
        // Add newly arrived processes to ready queue
        for (int i = 0; i < scheduler->size; i++) {
            Process *p = scheduler->processes[i];
            if (p->arrival_time == scheduler->current_time &&
                p->state == READY) {
                enqueue_process(ready_queue, p);
            }
        }

        // If no current process, get next from ready queue
        if (!current && ready_queue->size > 0) {
            current = dequeue_process(ready_queue);
            current->state = RUNNING;
        }

        // Process execution
        if (current) {
            current->remaining_time--;

            if (current->remaining_time == 0) {
                current->state = TERMINATED;
                current->turnaround_time =
                    scheduler->current_time - current->arrival_time + 1;
                current = NULL;
            }
        }

        // Update waiting time for processes in ready queue
        for (int i = 0; i < ready_queue->size; i++) {
            Process *p = ready_queue->queue[(ready_queue->front + i) %
                                         ready_queue->capacity];
            p->waiting_time++;
        }

        scheduler->current_time++;

        // Check if all processes are terminated
        bool all_terminated = true;
        for (int i = 0; i < scheduler->size; i++) {
            if (scheduler->processes[i]->state != TERMINATED) {
                all_terminated = false;
                break;
            }
        }

        if (all_terminated) break;
    }
}

FCFS算法简单易行,但并不总是能提供最佳性能,尤其是对于具有不同突发时间的进程。通过实现额外的调度功能,模拟器可以扩展以支持其他算法,如SJF和轮转算法。

5.优先级管理

基于优先级的调度将进程分配到不同的优先级级别,优先级较高的进程先被调度。MultilevelQueue结构管理多个优先级队列,而 schedule_priority 函数实现了优先级调度算法。

typedef struct {
    int priority_levels;
    ProcessQueue **queues;
} MultilevelQueue;

MultilevelQueue* create_multilevel_queue(int levels, int capacity) {
    MultilevelQueue *mlq = malloc(sizeof(MultilevelQueue));
    mlq->priority_levels = levels;
    mlq->queues = malloc(sizeof(ProcessQueue*) * levels);

    for (int i = 0; i < levels; i++) {
        mlq->queues[i] = create_queue(capacity);
    }

    return mlq;
}

void schedule_priority(Scheduler *scheduler, MultilevelQueue *mlq) {
    Process *current = NULL;

    while (true) {
        // Add newly arrived processes to appropriate priority queue
        for (int i = 0; i < scheduler->size; i++) {
            Process *p = scheduler->processes[i];
            if (p->arrival_time == scheduler->current_time &&
                p->state == READY) {
                int level = p->priority % mlq->priority_levels;
                enqueue_process(mlq->queues[level], p);
            }
        }

        // Select highest priority process
        if (!current) {
            for (int i = 0; i < mlq->priority_levels; i++) {
                if (mlq->queues[i]->size > 0) {
                    current = dequeue_process(mlq->queues[i]);
                    current->state = RUNNING;
                    break;
                }
            }
        }

        // Process execution logic...
    }
}

MultilevelQueue结构允许进程按优先级分组,确保高优先级进程先执行。调度优先级函数从队列中选择优先级最高的进程并执行它,提供公平且高效的调度机制。

6. 时间片控制

轮转调度使用固定的时间片来确保进程之间的公平CPU分配。schedule_round_robin函数通过管理就绪队列并强制执行每个进程的时间片来实现这一算法。

void schedule_round_robin(Scheduler *scheduler) {
    Process *current = NULL;
    ProcessQueue *ready_queue = create_queue(scheduler->capacity);
    int time_slice = 0;

    while (true) {
        // Process arrival and queue management
        for (int i = 0; i < scheduler->size; i++) {
            Process *p = scheduler->processes[i];
            if (p->arrival_time == scheduler->current_time &&
                p->state == READY) {
                enqueue_process(ready_queue, p);
            }
        }

        // Time quantum expiration check
        if (current && time_slice >= scheduler->time_quantum) {
            if (current->remaining_time > 0) {
                current->state = READY;
                enqueue_process(ready_queue, current);
            }
            current = NULL;
            time_slice = 0;
        }

        // Process selection
        if (!current && ready_queue->size > 0) {
            current = dequeue_process(ready_queue);
            current->state = RUNNING;
            time_slice = 0;
        }

        // Process execution
        if (current) {
            current->remaining_time--;
            time_slice++;

            if (current->remaining_time == 0) {
                current->state = TERMINATED;
                current->turnaround_time =
                    scheduler->current_time - current->arrival_time + 1;
                current = NULL;
                time_slice = 0;
            }
        }

        // Update statistics and check termination
        scheduler->current_time++;
        // ... rest of the implementation
    }
}

轮转算法通过将每个进程的执行时间限制在指定的时间量子内,确保没有进程垄断CPU。这种方法特别适用于时间共享系统,其中公平性和响应性至关重要。

7. 性能指标

模拟器包括一个SchedulerMetrics结构,用于跟踪关键性能指标,如平均等待时间、平均周转时间、CPU利用率和吞吐量。calculate_metrics函数根据调度器的状态计算这些指标。

typedef struct {
    double avg_waiting_time;
    double avg_turnaround_time;
    double cpu_utilization;
    double throughput;
    int context_switches;
} SchedulerMetrics;

SchedulerMetrics calculate_metrics(Scheduler *scheduler) {
    SchedulerMetrics metrics = {0};
    int total_waiting = 0;
    int total_turnaround = 0;
    int total_burst = 0;

    for (int i = 0; i < scheduler->size; i++) {
        Process *p = scheduler->processes[i];
        total_waiting += p->waiting_time;
        total_turnaround += p->turnaround_time;
        total_burst += p->burst_time;
    }

    metrics.avg_waiting_time = (double)total_waiting / scheduler->size;
    metrics.avg_turnaround_time = (double)total_turnaround / scheduler->size;
    metrics.cpu_utilization = (double)total_burst / scheduler->current_time * 100;
    metrics.throughput = (double)scheduler->size / scheduler->current_time;

    return metrics;
}

这些指标提供了有关调度程序性能的宝贵见解,并有助于确定改进领域。例如,平均等待时间过长可能表明调度算法未能有效管理进程执行。

8. 可视化系统

模拟器包括一个可视化系统,用于显示每个进程的调度时间线。可视化调度函数生成一个时间线,显示每个进程在每个时间单位的状态。

void visualize_schedule(Scheduler *scheduler) {
    printf("\nScheduling Timeline:\n");
    printf("Time: ");
    for (int t = 0; t < scheduler->current_time; t++) {
        printf("%-3d", t);
    }
    printf("\n");

    for (int i = 0; i < scheduler->size; i++) {
        Process *p = scheduler->processes[i];
        printf("P%-2d: ", p->pid);

        for (int t = 0; t < scheduler->current_time; t++) {
            char state = '.';
            if (t >= p->arrival_time) {
                if (p->execution_history[t] == RUNNING) state = 'R';
                else if (p->execution_history[t] == READY) state = 'r';
                else if (p->execution_history[t] == BLOCKED) state = 'b';
            }
            printf("%-3c", state);
        }
        printf("\n");
    }
}

9. 统计与分析

模拟器包括一个SchedulerAnalysis结构,用于对调度过程进行详细统计分析。analyze_scheduler函数计算响应时间、等待时间和周转时间的百分位数,以及公平指数,以评估调度器的公平性。

typedef struct {
    double response_time_percentile[3];  // 50th, 90th, 99th percentiles
    double waiting_time_percentile[3];
    double turnaround_time_percentile[3];
    double fairness_index;
} SchedulerAnalysis;

SchedulerAnalysis analyze_scheduler(Scheduler *scheduler) {
    SchedulerAnalysis analysis;
    int *response_times = malloc(sizeof(int) * scheduler->size);
    int *waiting_times = malloc(sizeof(int) * scheduler->size);
    int *turnaround_times = malloc(sizeof(int) * scheduler->size);

    // Collect data
    for (int i = 0; i < scheduler->size; i++) {
        Process *p = scheduler->processes[i];
        response_times[i] = p->first_run_time - p->arrival_time;
        waiting_times[i] = p->waiting_time;
        turnaround_times[i] = p->turnaround_time;
    }

    // Calculate percentiles
    qsort(response_times, scheduler->size, sizeof(int), compare_ints);
    qsort(waiting_times, scheduler->size, sizeof(int), compare_ints);
    qsort(turnaround_times, scheduler->size, sizeof(int), compare_ints);

    // Calculate fairness using Jain's fairness index
    analysis.fairness_index = calculate_fairness_index(scheduler);

    free(response_times);
    free(waiting_times);
    free(turnaround_times);

    return analysis;
}

该分析对调度器的性能进行了全面评估,有助于识别潜在的瓶颈和优化领域。

10. 总结

进程调度模拟器提供了一个全面了解和实验各种调度算法及其对系统性能影响的平台。实现包括进程管理的基本组件、不同的调度算法以及详细的性能分析工具。通过精心设计和实现这些机制,开发人员可以创建高效且公平的调度系统,以满足现代操作系统的需求。