作业调度和进程调度的简单模拟(C语言)

一、作业调度

1.题目描述

假定要在一台处理器上执行下表所示的作业,且假定这些作业在时刻 0 以 1、2、3、4、5 的顺序到达。说明分别使用 FCFSRR(时间片为 1)、SJF非剥夺式优先级调度算法时这些作业的执行情况(优先级从 1 到 5 依次降低)。针对上述每个调度算法,给出平均周转时间平均带权周转时间

作业 执行时间 优先级
1 10 3
2 1 1
3 2 3
4 1 4
5 5 2

注:题目来自王道《操作系统考研复习指导》

调度算法 { F i r s t   C o m e   F i r s t   S e r v e d :作业、进程调度,非抢占式,不会导致饥饿,有利于长作业和 C P U 繁忙型作业 S h o r t   J o b / P r o c e s s   F i r s t :作业、进程调度,非抢占式,会导致饥饿,有利于短作业 S h o r t e s t   R e m a i n i n g   T i m e   F i r s t :作业、进程调度,抢占式,会导致饥饿,有利于短作业 H i g h e s t   R e s p o n s e   R a t i o   N e x t :作业、进程调度,非抢占式,不会导致饥饿,响应比 = 等待时间 + 要求服务时间 要求服务时间 R o u n d   R o b i n :仅用于进程调度(只有进程才能分配时间片),抢占式,不会导致饥饿 P r i o r i t y   S c h e d u l i n g   A l g o r i t h m :作业、进程调度,会导致饥饿,有利于系统、 I / O 和前台等高优先级进程 M u l t i l e v e d   F e e d b a c k   Q u e u e :仅用于进程调度,抢占式,会导致饥饿 调度算法\begin{cases} \rm First\ Come\ First\ Served:作业、进程调度,非抢占式,不会导致饥饿,有利于长作业和CPU繁忙型作业\\ \rm Short\ Job/Process\ First:作业、进程调度,非抢占式,会导致饥饿,有利于短作业\\ \rm Shortest\ Remaining\ Time\ First:作业、进程调度,抢占式,会导致饥饿,有利于短作业\\ \rm Highest\ Response\ Ratio\ Next:作业、进程调度,非抢占式,不会导致饥饿,响应比=\frac{等待时间+要求服务时间}{要求服务时间}\\ \rm Round\ Robin:仅用于进程调度(只有进程才能分配时间片),抢占式,不会导致饥饿\\ \rm Priority\ Scheduling\ Algorithm:作业、进程调度,会导致饥饿,有利于系统、I/O和前台等高优先级进程\\ \rm Multileved\ Feedback\ Queue:仅用于进程调度,抢占式,会导致饥饿 \end{cases} 调度算法 First Come First Served:作业、进程调度,非抢占式,不会导致饥饿,有利于长作业和CPU繁忙型作业Short Job/Process First:作业、进程调度,非抢占式,会导致饥饿,有利于短作业Shortest Remaining Time First:作业、进程调度,抢占式,会导致饥饿,有利于短作业Highest Response Ratio Next:作业、进程调度,非抢占式,不会导致饥饿,响应比=要求服务时间等待时间+要求服务时间Round Robin:仅用于进程调度(只有进程才能分配时间片),抢占式,不会导致饥饿Priority Scheduling Algorithm:作业、进程调度,会导致饥饿,有利于系统、I/O和前台等高优先级进程Multileved Feedback Queue:仅用于进程调度,抢占式,会导致饥饿

2.代码实现

通过代码对四种调度算法进行简单模拟,并非对调度算法的实现,同时输出每次的调度信息,计算相关周转参数。

#include 
#include 
#include 
#include 

#define JOB_NUM 5 // 作业数量

/* 作业结构体的定义 */
typedef struct Job {
    char name[3]; // 作业名称
    int priority; // 作业优先级
    int arr_order; // 到达次序
    int cos_time; // 作业执行时间
    int rem_time; // 作业剩余时间
} Job;

/* 时间片轮转队列节点定义 */
typedef struct QueueNode {
    Job data; // 数据域
    struct QueueNode *next; // 指针域
} QueueNode;

/* 时间片轮转队列定义 */
typedef struct Queue {
    QueueNode *head, *tail; // 队列指针
} Queue;

/* 初始化单个作业的信息 */
void jobInit(Job *job, char *name, int arr_order, int cos_time, int priority);

/* 初始化作业列表中的作业信息 */
void jobListInit(Job *job_list);

/* 时间片轮转队列初始化 */
void queueInit(Queue *queue);

/* 时间片轮转队列销毁 */
void queueDestroy(Queue *queue);

/* 时间片轮转队列判空 */
bool queueIsEmpty(Queue queue);

/* 时间片轮转队列入队 */
void queueEmplace(Queue *queue, Job job);

/* 时间片轮转队列出队 */
Job queuePop(Queue *queue);

/* 先来先服务 */
void FCFS(Job *job_list);

/* 时间片轮转调度算法 */
void RR(Job *job_list);

/* 短作业优先 */
void SJF(Job *job_list);

/* 优先级调度 */
void PSA(Job *job_list);

int main(int argc, char *argv[]) {
    if (argc == 1) {
        printf("请输入使用的调度算法!\n");
        return 0;
    }

    Job job_list[JOB_NUM];

    jobListInit(job_list);

    if (strcmp(argv[1], "FCFS") == 0) {
        FCFS(job_list);
    } else if (strcmp(argv[1], "RR") == 0) {
        RR(job_list);
    } else if (strcmp(argv[1], "SJF") == 0) {
        SJF(job_list);
    } else {
        PSA(job_list);
    }

    return 0;
}

void jobInit(Job *job, char *name, int arr_order, int cos_time, int priority) {
    strcpy(job->name, name);
    job->priority = priority;
    job->arr_order = arr_order;
    job->cos_time = cos_time;
    job->rem_time = cos_time;
}

void jobListInit(Job *job_list) {
    jobInit(job_list + 0, "J1", 1, 10, 3);
    jobInit(job_list + 1, "J2", 2, 1, 1);
    jobInit(job_list + 2, "J3", 3, 2, 3);
    jobInit(job_list + 3, "J4", 4, 1, 4);
    jobInit(job_list + 4, "J5", 5, 5, 2);
}

void queueInit(Queue *queue) {
    queue->head = queue->tail = (QueueNode *) malloc(sizeof(QueueNode));
    queue->head->next = NULL;
}

void queueDestroy(Queue *queue) {
    QueueNode *cur = queue->head;
    while (cur) {
        QueueNode *tmp = cur;
        free(tmp);
        cur = cur->next;
    }
}

bool queueIsEmpty(Queue queue) {
    return (queue.head == queue.tail) ? true : false;
}

void queueEmplace(Queue *queue, Job job) {
    QueueNode *new_node = (QueueNode *) malloc(sizeof(QueueNode));
    new_node->data = job;
    new_node->next = NULL;
    queue->tail->next = new_node;
    queue->tail = queue->tail->next;
}

Job queuePop(Queue *queue) {
    if (queue->tail == queue->head->next) { // 本次出队会导致队空
        queue->tail = queue->head;
    }
    Job job = queue->head->next->data;
    QueueNode *tmp = queue->head->next;
    queue->head->next = tmp->next;
    free(tmp);
    return job;
}

void FCFS(Job *job_list) {
    /* 根据到达次序对作业进行排序 */
    for (int i = 0; i < JOB_NUM - 1; i++) {
        for (int j = 0; j < JOB_NUM - 1 - i; j++) {
            if (job_list[j].arr_order > job_list[j + 1].arr_order) {
                Job tmp = job_list[j];
                job_list[j] = job_list[j + 1];
                job_list[j + 1] = tmp;
            }
        }
    }

    double turnaround_time_total = 0; // 周转时间之和
    double weighted_turnaround_time_total = 0; // 带权周转时间之和
    int current_time = 0; // 当前时刻

    for (int i = 0; i < JOB_NUM; i++) {
        printf("第%d次调度:\n当前执行作业:%s\n等待作业队列:", i + 1, job_list[i].name);
        for (int j = i + 1; j < JOB_NUM; j++) {
            printf("%s%c", job_list[j].name, (j == JOB_NUM - 1) ? 0 : 32);
        }
        printf("\n\n");

        current_time += job_list[i].cos_time; // 更新时刻
        turnaround_time_total += current_time; // 累加周转时间
        weighted_turnaround_time_total += current_time * 1.0 / job_list[i].cos_time; // 累加带权周转时间
    }

    printf("平均周转时间:%.2f\n", turnaround_time_total / JOB_NUM);
    printf("平均带权周转时间:%.2f\n", weighted_turnaround_time_total / JOB_NUM);
}

void RR(Job *job_list) {
    /* 根据到达次序对作业进行排序 */
    for (int i = 0; i < JOB_NUM - 1; i++) {
        for (int j = 0; j < JOB_NUM - 1 - i; j++) {
            if (job_list[j].arr_order > job_list[j + 1].arr_order) {
                Job tmp = job_list[j];
                job_list[j] = job_list[j + 1];
                job_list[j + 1] = tmp;
            }
        }
    }

    Queue queue;
    queueInit(&queue);
    for (int i = 0; i < JOB_NUM; i++) {
        queueEmplace(&queue, job_list[i]);
    }

    double turnaround_time_total = 0; // 周转时间之和
    double weighted_turnaround_time_total = 0; // 带权周转时间之和
    int current_time = 0; // 当前时刻
    int count = 1; // 调度次数计数器

    while (!queueIsEmpty(queue)) {
        /* 轮转队列中仅剩最后一个作业 进行最后一次调度 */
        if (queue.head->next == queue.tail) {
            printf("第%d次调度:\n", count++);
            Job now_job = queuePop(&queue);
            printf("当前执行作业:%s\n", now_job.name);
            printf("等待作业队列:\n\n");
            turnaround_time_total += current_time + now_job.rem_time; // 累加周转时间
            weighted_turnaround_time_total += (current_time + now_job.rem_time) * 1.0 / now_job.cos_time; // 累加带权周转时间
            break;
        }
        printf("第%d次调度:\n", count++);
        Job now_job = queuePop(&queue);
        printf("当前执行作业:%s\n", now_job.name);
        now_job.rem_time -= 1;
        current_time++;
        printf("等待作业队列:");
        for (QueueNode *cur = queue.head->next; cur != NULL; cur = cur->next) {
            printf("%s%c", cur->data.name, (cur->next == NULL) ? 0 : 32);
        }
        printf("\n\n");
        if (now_job.rem_time > 0) {
            queueEmplace(&queue, now_job);
        } else {
            turnaround_time_total += current_time; // 累加周转时间
            weighted_turnaround_time_total += current_time * 1.0 / now_job.cos_time; // 累加带权周转时间
        }
    }

    queueDestroy(&queue);

    printf("平均周转时间:%.2f\n", turnaround_time_total / JOB_NUM);
    printf("平均带权周转时间:%.2f\n", weighted_turnaround_time_total / JOB_NUM);
}

void SJF(Job *job_list) {
    /* 根据消耗时间对作业进行排序 优先考虑稳定排序 */
    for (int i = 0; i < JOB_NUM - 1; i++) {
        for (int j = 0; j < JOB_NUM - 1 - i; j++) {
            if (job_list[j].cos_time > job_list[j + 1].cos_time) {
                Job tmp = job_list[j];
                job_list[j] = job_list[j + 1];
                job_list[j + 1] = tmp;
            }
        }
    }

    double turnaround_time_total = 0; // 周转时间之和
    double weighted_turnaround_time_total = 0; // 带权周转时间之和
    int current_time = 0; // 当前时刻

    for (int i = 0; i < JOB_NUM; i++) {
        printf("第%d次调度:\n当前执行作业:%s\n等待作业队列:", i + 1, job_list[i].name);
        for (int j = i + 1; j < JOB_NUM; j++) {
            printf("%s%c", job_list[j].name, (j == JOB_NUM - 1) ? 0 : 32);
        }
        printf("\n\n");

        current_time += job_list[i].cos_time; // 更新时刻
        turnaround_time_total += current_time; // 累加周转时间
        weighted_turnaround_time_total += current_time * 1.0 / job_list[i].cos_time; // 累加带权周转时间
    }

    printf("平均周转时间:%.2f\n", turnaround_time_total / JOB_NUM);
    printf("平均带权周转时间:%.2f\n", weighted_turnaround_time_total / JOB_NUM);
}

void PSA(Job *job_list) {
    /* 根据优先级对作业进行排序 优先考虑稳定排序 */
    for (int i = 0; i < JOB_NUM - 1; i++) {
        for (int j = 0; j < JOB_NUM - 1 - i; j++) {
            if (job_list[j].priority > job_list[j + 1].priority) {
                Job tmp = job_list[j];
                job_list[j] = job_list[j + 1];
                job_list[j + 1] = tmp;
            }
        }
    }

    double turnaround_time_total = 0; // 周转时间之和
    double weighted_turnaround_time_total = 0; // 带权周转时间之和
    int current_time = 0; // 当前时刻

    for (int i = 0; i < JOB_NUM; i++) {
        printf("第%d次调度:\n当前执行作业:%s\n等待作业队列:", i + 1, job_list[i].name);
        for (int j = i + 1; j < JOB_NUM; j++) {
            printf("%s%c", job_list[j].name, (j == JOB_NUM - 1) ? 0 : 32);
        }
        printf("\n\n");

        current_time += job_list[i].cos_time; // 更新时刻
        turnaround_time_total += current_time; // 累加周转时间
        weighted_turnaround_time_total += current_time * 1.0 / job_list[i].cos_time; // 累加带权周转时间
    }

    printf("平均周转时间:%.2f\n", turnaround_time_total / JOB_NUM);
    printf("平均带权周转时间:%.2f\n", weighted_turnaround_time_total / JOB_NUM);
}

运行结果示例如下:

atreus@MacBook-Pro % clang 1.1.c -o exe.out
atreus@MacBook-Pro % ./exe.out FCFS
第1次调度:
当前执行作业:J1
等待作业队列:J2 J3 J4 J5

第2次调度:
当前执行作业:J2
等待作业队列:J3 J4 J5

第3次调度:
当前执行作业:J3
等待作业队列:J4 J5

第4次调度:
当前执行作业:J4
等待作业队列:J5

第5次调度:
当前执行作业:J5
等待作业队列:

平均周转时间:13.40
平均带权周转时间:7.26
atreus@MacBook-Pro % ./exe.out RR  
第1次调度:
当前执行作业:J1
等待作业队列:J2 J3 J4 J5

第2次调度:
当前执行作业:J2
等待作业队列:J3 J4 J5 J1

第3次调度:
当前执行作业:J3
等待作业队列:J4 J5 J1

第4次调度:
当前执行作业:J4
等待作业队列:J5 J1 J3

第5次调度:
当前执行作业:J5
等待作业队列:J1 J3

第6次调度:
当前执行作业:J1
等待作业队列:J3 J5

第7次调度:
当前执行作业:J3
等待作业队列:J5 J1

第8次调度:
当前执行作业:J5
等待作业队列:J1

第9次调度:
当前执行作业:J1
等待作业队列:J5

第10次调度:
当前执行作业:J5
等待作业队列:J1

第11次调度:
当前执行作业:J1
等待作业队列:J5

第12次调度:
当前执行作业:J5
等待作业队列:J1

第13次调度:
当前执行作业:J1
等待作业队列:J5

第14次调度:
当前执行作业:J5
等待作业队列:J1

第15次调度:
当前执行作业:J1
等待作业队列:

平均周转时间:9.20
平均带权周转时间:2.84
atreus@MacBook-Pro % ./exe.out SJF
第1次调度:
当前执行作业:J2
等待作业队列:J4 J3 J5 J1

第2次调度:
当前执行作业:J4
等待作业队列:J3 J5 J1

第3次调度:
当前执行作业:J3
等待作业队列:J5 J1

第4次调度:
当前执行作业:J5
等待作业队列:J1

第5次调度:
当前执行作业:J1
等待作业队列:

平均周转时间:7.00
平均带权周转时间:1.74
atreus@MacBook-Pro % ./exe.out PSA
第1次调度:
当前执行作业:J2
等待作业队列:J5 J1 J3 J4

第2次调度:
当前执行作业:J5
等待作业队列:J1 J3 J4

第3次调度:
当前执行作业:J1
等待作业队列:J3 J4

第4次调度:
当前执行作业:J3
等待作业队列:J4

第5次调度:
当前执行作业:J4
等待作业队列:

平均周转时间:12.00
平均带权周转时间:6.36
atreus@MacBook-Pro % 

二、作业调度进程调度混合

1.题目描述

有一个两道批处理系统,它只有一个 CPU(一次只能处理一个进程),作业调度算法采用短作业优先调度进程调度算法采用抢占式优先级调度(优先数越小、优先级越高)。假设有四个作业J1、J2、J3、J4,其运行情况如下表所示,请输出作业调度和进程调度状态并计算平均周转时间。

作业名 到达时间 运行时间 优先数
J1 0 40 5
J2 20 30 3
J3 30 50 4
J4 50 20 6

2.代码实现

#include 
#include 

#define N 4

enum job_state {
    WAIT = 0, // 作业在外存等待调度
    READY = 1, // 已经为作业分配线程 处于就绪态 由于SJF为非抢占 作业不会被换出内存
    RUN = 2, // 运行态 可能会被高优先级进程抢占
    FINISH = 3 // 作业已经完成
};

/* 作业结构体的定义 */
typedef struct Job {
    char name[3]; // 作业名称
    int priority; // 作业优先级
    int arr_time; // 作业到达时间
    int cos_time; // 作业执行时间
    int rem_time; // 作业剩余时间
    enum job_state state; // 作业状态
} Job;

Job job_list[N] = {{"J1", 5, 0,  40, 40, WAIT},
                   {"J2", 3, 20, 30, 30, WAIT},
                   {"J3", 4, 30, 50, 50, WAIT},
                   {"J4", 6, 50, 20, 20, WAIT}};

/**
 * 作业调度 采用 短作业优先 调度算法
 * 进程调度 采用 抢占式优先级 调度算法
 */
int main() {
    int mem_space = 2; // 内存中还可存放的作业数
    int task_num = N; // 待处理作业总数
    int finish_count = 0; // 记录已经完成任务的作业数
    int cur_time = 0; // 实时时间
    int sum_time = 0; // 周转时间和

    /* 通过while(1)循环模拟系统执行 循环周期为1个单位时间 当所有作业完成时退出 */
    while (1) {
        /* 作业调度 */
        /* 按照消耗时间从小到大对任务进行排序 优先调度短作业 */
        for (int i = 0; i < task_num; i++) {
            for (int j = 0; j < task_num - i - 1; j++) {
                if (job_list[j].cos_time > job_list[j + 1].cos_time) {
                    Job tmp = job_list[j];
                    job_list[j] = job_list[j + 1];
                    job_list[j + 1] = tmp;
                }
            }
        }

        /* 选取小于等于mem_space个作业放入内存 并将其标记为READY状态 */
        /* 由于已经提前进行排序 所以遍历时只要满足arr_time <= cur_time就可以调入内存 */
        int tmp_count = mem_space;
        for (int i = 0; i < tmp_count; i++) {
            for (int j = 0; j < task_num; j++) {
                if (job_list[j].arr_time <= cur_time && job_list[j].state == WAIT) { // 作业已经到达且在外存等待
                    printf("作业调度:%d-%s\n", cur_time, job_list[j].name);
                    job_list[j].state = READY;
                    mem_space--;
                    break;
                }
            }
        }

        /* 进程调度 */
        for (int i = 0; i < task_num; i++) {
            for (int j = 0; j < task_num - i - 1; j++) {
                if (job_list[j].priority > job_list[j + 1].priority) {
                    Job tmp = job_list[j];
                    job_list[j] = job_list[j + 1];
                    job_list[j + 1] = tmp;
                }
            }
        }
        /* 由于已经提前进行排序 所以遍历时只要满足状态为READY或RUN就可以调入CPU执行 */
        int add_time = INT32_MAX; // 本轮执行需要消耗的时间
        for (int i = 0; i < task_num; i++) {
            if (job_list[i].state == READY) { // 从内存调入CPU执行
                cur_time++;
                /* 检查当前进程放入CPU是否会抢占其他进程 */
                for (int j = i + 1; j < task_num; j++) {
                    if (job_list[j].state == RUN) {
                        job_list[j].state = READY;
                        break;
                    }
                }
                job_list[i].state = RUN;
                if (--job_list[i].rem_time == 0) {
                    job_list[i].state = FINISH;
                    finish_count++;
                    sum_time += (cur_time - job_list[i].arr_time);
                    mem_space++;
                }
                printf("进程调度:%d-%s\n", cur_time, job_list[i].name);
                break;
            } else if (job_list[i].state == RUN) { // 在CPU上维持执行
                cur_time++;
                if (--job_list[i].rem_time == 0) {
                    job_list[i].state = FINISH;
                    finish_count++;
                    sum_time += (cur_time - job_list[i].arr_time);
                    mem_space++;
                }
                break;
            }
        }

        if (finish_count == 4) {
            break;
        }
    }
    printf("平均周转时间为:%.2f\n", sum_time * 1.0 / task_num);
}

运行结果示例如下:

atreus@MacBook-Pro % clang 1.2.c -o exe.out
atreus@MacBook-Pro % ./exe.out 
作业调度:0-J1
进程调度:1-J1
作业调度:20-J2
进程调度:21-J2
作业调度:50-J4
进程调度:51-J1
作业调度:70-J3
进程调度:71-J3
进程调度:121-J4
平均周转时间为:70.00
atreus@MacBook-Pro % 

你可能感兴趣的:(数据结构与算法,c语言,操作系统,队列)