目录
先来先服务调度(First-Come-First-Served, FCFS)
最短作业优先调度(Shortest-Job-First, SJF)
优先级调度(Priority-Scheduling, PS)
轮转调度(Round-Robin, RR)
先来先服务调度(First-Come-First-Served, FCFS)
- 非抢占(FCFS)
最短作业优先调度(Shortest-Job-First, SJF)
- 非抢占(nonpremptive_SJF)
- 抢占(premptive_SJF)
优先级调度(Priority-Scheduling, PS)
- 非抢占(nonpremptive_PS)
- 抢占(premptive_PS)
轮转调度(Round-Robin, RR)
- 抢占(RR)
例子
甘特图
说明
- 为了简便起见,所有进程只保留与调度算法相关的属性,即进程号、到达时间、剩余执行时间、已经执行时间、已经等待时间(未使用)、优先级。
- 用单向链表表示队列。
- 为了简便起见,进程要么处于运行状态,要么在队列中等待状态,同一时刻只有一个进程处于运行状态。
- 为了简便起见,在初始时刻,无论进程是否到达,先把所有进程加入队列(不足之处)。
- 记当前时间为timer,用来与进程到达时间进行比较,判断进程是否到达。
- 假设进程下一次CPU执行时间(CPU burst)已经知道。
- 用户只能设置静态优先级nice,而动态优先级priority通过计算得出(只进行简单的模拟)。nice用低数字表示高优先级。
缺点
- 优先级反转问题没有解决
代码
process.h
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#define NZERO 20
#define PRI_USER_MAX 127
#define PRI_USER_MIN 0
typedef struct{
int pid; // 进程号
int arrival_time; // 到达时间
int remaining_execution_time; // 剩余执行时间
int execution_time; // 已经执行时间
int waiting_time; // 已经等待时间
int nice; // 静态优先级(数值越低,优先级越高)
int priority; // 动态优先级(数值越高,优先级越高)
}process;
process create_process(); // 创建一个进程
void setpriority(process *p, int prio); // 设置静态优先级(-NZERO, NZERO - 1)
int getpriority(process *p); // 获取静态优先级
void running_process(process *p); // 运行一个时间片
void waiting_process(process *p); // 等待一个时间片
process create_process(){
printf("creating a process....\n");
process p;
printf("input pid of process: ");
scanf("%d", &p.pid);
printf("input arrival time of the process: ");
scanf("%d", &p.arrival_time);
printf("input remaining execution time of the process: ");
scanf("%d", &p.remaining_execution_time);
if(p.remaining_execution_time <= 0){
printf("create process error! Remaining execution time <= 0");
exit(-1);
}
p.execution_time = 0;
p.waiting_time = 0;
printf("input priority of the process: ");
int prio;
scanf("%d", &prio);
setpriority(&p, prio);
p.priority = PRI_USER_MAX - p.execution_time / 4 - p.nice * 2;
return p;
}
void setpriority(process *p, int prio){
if(prio < 0 || prio >= 2*NZERO){
printf("setpriority error!");
exit(0);
}
p->nice = prio - NZERO;
}
int getpriority(process *p){
return p->nice + NZERO;
}
void running_process(process *p){
printf("running process %d......\n", p->pid);
// 更新剩余执行时间和已经执行时间,和动态优先级
// sleep(1);
p->remaining_execution_time--;
p->execution_time++;
p->priority = PRI_USER_MAX - p->execution_time / 4 - p->nice * 2;
}
void waiting_process(process *p){
// 更新等待时间
// sleep(1);
p->waiting_time++;
}
process_queue.h
#include"process.h"
typedef struct node{
process value;
struct node *next;
}node;
typedef struct{
node *head; // 头节点
node *tail; // 尾节点
node *curr; // 当前节点(为了方便进行插入或删除操作,实际上操作的是当前节点的下一个节点)
int count; // 链表当前节点数(空节点除外)
}process_queue;
process_queue create(); // 创建链表
void clear(process_queue *q); // 清空链表
void append(process_queue *q, process value); // 在链表末尾添加节点
void insert(process_queue *q, process value); // 在当前位置插入节点
process delete(process_queue *q); // 在当前位置删除节点
void moveToStart(process_queue *q); // 移到表头
void moveToEnd(process_queue *q); // 移到表位
void moveToPos(process_queue *q, int pos); // 移到指定位置(pos从0开始)
int currPos(process_queue q); // 当前位置
void next(process_queue *q); // 当前位置向后移
process getValue(process_queue q); // 获取当前节点的元素
process FCFS_POP(process_queue *q, int timer); // 最先到达的进程
process SJF_POP(process_queue *q, int timer); // 剩余执行时间最短的进程
process PS_POP(process_queue *q, int timer); // 优先级最高的进程
process RR_POP(process_queue *q, int timer); // 轮流执行
void waiting_queue(process_queue *q); // 队列中所有进程等待一个时间片
void init(process_queue *q){
// 当链表为空时,头节点、尾节点、当前节点无处可指,在插入删除操作时,需要增加额外的代码。
// 为此,增加一个空节点
node *n = (node *)malloc(sizeof(node));
n->next = NULL;
// 头节点,尾节点,当前节点指向空节点
q->head = n;
q->curr = n;
q->tail = n;
q->count = 0;
}
void removeall(process_queue *q){
// 只要链表不为空,不断地从头删除节点
while(q->head != NULL){
q->curr = q->head;
q->head = q->head->next;
free(q->curr);
}
}
process_queue create(){
process_queue *q = (process_queue *)malloc(sizeof(process_queue));
init(q);
return *q;
}
void clear(process_queue *q){
removeall(q);
init(q);
}
void append(process_queue *q, process value){
node *n = (node *)malloc(sizeof(node));
n->value = value;
n->next = NULL;
// 让链表尾节点指向它(注意指针与实际物理内存的联系,不然会产生疑惑)
q->tail->next = n;
q->tail = q->tail->next;
q->count++;
}
void insert(process_queue *q, process value){
node *n = (node *)malloc(sizeof(node));
n->value = value;
n->next = q->curr->next;
q->curr->next = n;
// 如果当前节点为尾节点,那么尾节点需要指向该节点(尾节点始终应该在链表末尾)
if(q->curr == q->tail){
q->tail = q->curr->next;
}
q->count++;
}
process delete(process_queue *q){
// getValue已经判断了是否为空的情况
process value = getValue(*q);
node *n = q->curr->next;
// 如果待删除的节点为尾节点,那么尾节点需要指向当前节点(因为尾节点不应该无处可指)
if(q->curr->next == q->tail){
q->tail = q->curr;
}
q->curr->next = q->curr->next->next;
free(n);
q->count--;
return value;
}
void moveToStart(process_queue *q){
q->curr = q->head;
}
void moveToEnd(process_queue *q){
q->curr = q->tail;
}
void moveToPos(process_queue *q, int pos){
if(pos < 0 || pos >= q->count){
printf("position out of range");
exit(0);
}
moveToStart(q);
int i = 0;
while(i < pos){
q->curr = q->curr->next;
i++;
}
}
int currPos(process_queue q){
node *temp = q.head;
int i;
for(i = 0; q.curr != temp; i++){
temp = temp->next;
}
return i;
}
void next(process_queue *q){
if(q->curr != q->tail){
q->curr = q->curr->next;
}
}
process getValue(process_queue q){
if(q.curr->next == NULL){
printf("No value");
exit(0);
}
// 返回的是当前节点的下一个节点的值
return q.curr->next->value;
}
process FCFS_POP(process_queue *q, int timer){
int pos = 0; // 记录最先到达的进程所在的位置
int i = 0; // 记录当前位置
int arrival_time = INT_MAX;
moveToStart(q);
while(q->curr != q->tail){
// 只能在已经到达的进程中寻找
if(getValue(*q).arrival_time <= timer && getValue(*q).arrival_time < arrival_time){
arrival_time = getValue(*q).arrival_time;
pos = i;
}
i++;
next(q);
}
// 如果当前没有进程,则返回队列中第一个进程
moveToPos(q, pos);
return delete(q);
}
process SJF_POP(process_queue *q, int timer){
int pos = 0; // 记录剩余执行时间最短的进程所在的位置
int i = 0; // 记录当前位置
int remaining_execution_time = INT_MAX;
moveToStart(q);
while(q->curr != q->tail){
if(getValue(*q).arrival_time <= timer && getValue(*q).remaining_execution_time < remaining_execution_time){
remaining_execution_time = getValue(*q).remaining_execution_time;
pos = i;
}
i++;
next(q);
}
// 如果当前没有进程,则返回队列中第一个进程
moveToPos(q, pos);
return delete(q);
}
process PS_POP(process_queue *q, int timer){
int pos = 0; // 记录剩余执行时间最短的进程所在的位置
int i = 0; // 记录当前位置
int priority = INT_MIN;
moveToStart(q);
while(q->curr != q->tail){
if(getValue(*q).arrival_time <= timer && getValue(*q).priority > priority){
priority = getValue(*q).priority;
pos = i;
}
i++;
next(q);
}
// 如果当前没有进程,则返回队列中第一个进程
moveToPos(q, pos);
return delete(q);
}
process RR_POP(process_queue *q, int timer){
moveToStart(q);
while(q->curr != q->tail){
if(getValue(*q).arrival_time <= timer){
break;
}
next(q);
}
return delete(q);
}
void waiting_queue(process_queue *q){
int pos = currPos(*q);
for(moveToStart(q); q->curr != q->tail; next(q)){
waiting_process(&(q->curr->value));
}
if(pos < q->count){
moveToPos(q, pos);
}
}
schedule.h
#include"process_queue.h"
int timer = 0; // 当前时间
void FCFS(process_queue q); // First-Come-First-Served
void nonpremptive_SJF(process_queue q); // nonpremptive Shortest-Job-First
void premptive_SJF(process_queue q); // premptive Shortest-Job-First
void nonpremptive_PS(process_queue q); // nonpremptive Priority-Scheduling
void premptive_PS(process_queue q); // premptive Priority-Scheduling
void RR(process_queue q); // Round-Robin
void FCFS(process_queue q){
while(q.count > 0){
// 选择方式--选择队列中第一个到达的进程,直到该进程执行结束
process p = FCFS_POP(&q, timer);
// 如果当前没有进程到达
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
while(p.remaining_execution_time > 0){
// 某一时刻,只有一个进程处于运行状态,其他进程处于等待状态
running_process(&p);
waiting_queue(&q);
timer++;
}
}
}
void nonpremptive_SJF(process_queue q){
while(q.count > 0){
// 选择方式--选择队列中剩余执行时间最短的进程
process p = SJF_POP(&q, timer);
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
// 执行该进程直到结束
while(p.remaining_execution_time > 0){
// 某一时刻,只有一个进程处于运行状态,其他进程处于等待状态
running_process(&p);
waiting_queue(&q);
timer++;
}
}
}
void premptive_SJF(process_queue q){
while(q.count > 0){
// 选择方式--选择队列中剩余执行时间最短的进程
process p = SJF_POP(&q, timer);
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
// 执行该进程一个时间片
running_process(&p);
waiting_queue(&q);
timer++;
// 如果进程没有执行完成,返回到队列原位置
if(p.remaining_execution_time > 0){
insert(&q, p);
}
}
}
void nonpremptive_PS(process_queue q){
while(q.count > 0){
// 选择方式--选择队列中优先级最高的进程
process p = PS_POP(&q, timer);
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
// 执行该进程直到结束
while(p.remaining_execution_time > 0){
// 某一时刻,只有一个进程处于运行状态,其他进程处于等待状态
running_process(&p);
waiting_queue(&q);
timer++;
}
}
}
void premptive_PS(process_queue q){
while(q.count > 0){
// 选择方式--选择队列中优先级最高的进程
process p = PS_POP(&q, timer);
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
// 执行该进程一个时间片
running_process(&p);
waiting_queue(&q);
timer++;
// 如果进程没有执行完成,返回到队列原位置
if(p.remaining_execution_time > 0){
insert(&q, p);
}
}
}
void RR(process_queue q){
while(q.count > 0){
// 选择方式--轮流执行队列中的进程(队列中第一个到达的进程)
process p = RR_POP(&q, timer);
if(p.arrival_time > timer){
timer++;
printf("No process running......\n");
insert(&q, p);
continue;
}
// 执行该进程一个时间片
running_process(&p);
waiting_queue(&q);
timer++;
// 如果进程没有执行完成,返回到队列末尾
if(p.remaining_execution_time > 0){
append(&q, p);
}
}
}
测试代码
#include"schedule.h"
int main(){
process p[3];
process_queue q = create();
for(int i = 0; i < 3; i++){
p[i] = create_process();
append(&q, p[i]);
}
// FCFS(q);
// nonpremptive_SJF(q);
// premptive_SJF(q);
// nonpremptive_PS(q);
// premptive_PS(q);
RR(q);
return 0;
}
修改
2021/9/8
轮转调度选择一个进程,执行完一个时间片后,返回队列末尾,而不是原来的位置。这样一来,每次只需要选择队列中第一个到达的进程即可。
2021/9/10
区分了静态优先级和动态优先级,一定程度上(实现的比较粗糙)解决饥饿现象。