假定要在一台处理器上执行下表所示的作业,且假定这些作业在时刻 0 以 1、2、3、4、5 的顺序到达。说明分别使用 FCFS、RR(时间片为 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:仅用于进程调度,抢占式,会导致饥饿
通过代码对四种调度算法进行简单模拟,并非对调度算法的实现,同时输出每次的调度信息,计算相关周转参数。
#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 %
有一个两道批处理系统,它只有一个 CPU(一次只能处理一个进程),作业调度算法采用短作业优先调度,进程调度算法采用抢占式优先级调度(优先数越小、优先级越高)。假设有四个作业J1、J2、J3、J4,其运行情况如下表所示,请输出作业调度和进程调度状态并计算平均周转时间。
作业名 | 到达时间 | 运行时间 | 优先数 |
---|---|---|---|
J1 | 0 | 40 | 5 |
J2 | 20 | 30 | 3 |
J3 | 30 | 50 | 4 |
J4 | 50 | 20 | 6 |
#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 %