实验报告
实验内容
CPU 调度。
基本问题:讨论课件 Lecture19-20 中 CPU 调度算法的例子,尝试基于 POSIX API 设计一个简单调度器(不考虑资源竞争问题):
-
创建一些 Pthread 线程任务,建立一个管理链队列,结点内容起码包括到达时间、WCT、优先级、调度状态(运行、就绪、阻塞)等调度参数;
-
每个任务有一个调度信号量,任务启动后在其调度信号量上执行 wait;
-
调度器按照调度策略对处于运行态的任务(如果有的话)的调度信号量执行 wait,并选取适当任务的调度信号量执行 signal;
-
实现简单调度策略:FCFS、SJF、Priority。分别计算任务平均等待时间。
实验环境
Ubuntu 20.04.2.0(64位)
基础知识
- FCFS(First-Come, First-Served):队列中到达时间早的先执行。
例:
- SJF(Shortest Job First):非抢占的情况下,队列中执行时间短的线程先执行。
例:
- Priority:非抢占的情况下,队列中优先级高(priority小)的线程先执行。
例:假设到达时间都一样
实验设计
(详细过程请看代码注释)
- scheduler.h
#ifndef _SCHEDULER_H_
#define _SCHEDULER_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define MAX_TASK 1000
#define running 1
#define ready 0
#define block -1
// structure of thread
typedef struct task
{
int taskName;
int arriveTime;
int needTime;
int priority;
int stat;
pthread_t ptid;
struct task *next;
} Task;
// structure of scheduling queue
typedef struct queue
{
Task *head;
Task *tail;
int waitTime; // total waiting time
int CPUTime; // CPU working time
} Queue;
void FCFS(Queue *q);
void SJF(Queue *q);
void Priority(Queue *q);
#endif
- scheduler.c
#include "scheduler.h"
#include <time.h>
// sort by arrival time
void sortArriveTime(Task *task, int n)
{
Task temp;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (task[j].arriveTime > task[j + 1].arriveTime)
{
temp = task[j];
task[j] = task[j + 1];
task[j + 1] = temp;
}
}
}
}
// In SJF mode: Find the thread with the shortest execution time in the current queue under the CPU time
Task *SJFfindNextJob(Queue *q)
{
Task *temp = q->head;
Task *minTask = q->head; // thread with the shortest need time
while (temp != NULL && temp->arriveTime <= q->CPUTime) // if thread's arriveTime > CPUTime, this thread is not in the queue
{
if (temp->needTime < minTask->needTime)
{
minTask = temp;
}
temp = temp->next;
}
return minTask;
}
// In Priority mode: Find the thread with the highest priority(the value of priority is smallest) in the current queue under the CPU time
Task *PfindNextJob(Queue *q)
{
Task *temp = q->head;
Task *highTask = q->head;
while (temp != NULL && temp->arriveTime <= q->CPUTime) // if thread's arriveTime > CPUTime, this thread is not in the queue
{
if (temp->priority < highTask->priority)
{
highTask = temp;
}
temp = temp->next;
}
return highTask;
}
// thread's working function
void *runner(void *args)
{
Task *temp = (Task *)args;
temp->stat = running;
printf("Thread %d is running. Running time is: %ds.\n", temp->taskName, temp->needTime);
return NULL;
}
int main()
{
int n; // the number of threads
printf("Please enter the amount of tasks(1~1000): ");
scanf("%d", &n);
if (n < 1 || n > 1000)
{
printf("ERROR VALUE!\n");
return -1;
}
printf("\n");
Task *task = (Task *)malloc(sizeof(Task) * n); // the array of thread
for (int i = 0; i < n; i++)
{
printf("Please enter arrive time and burst time of task %d: ", i);
scanf("%d %d", &task[i].arriveTime, &task[i].needTime);
task[i].taskName = i;
srand((unsigned)time(NULL));
task[i].priority = rand() % 100;
task[i].stat = ready;
task[i].next = NULL;
}
sortArriveTime(task, n);
printf("\n");
Queue *q = (Queue *)malloc(sizeof(Queue)); // scheduling queue
q->head = NULL;
q->tail = NULL;
q->waitTime = 0;
q->CPUTime = 0;
for (int i = 0; i < n; i++) // add thread to scheduling queue
{
if (q->head == NULL)
{
q->head = &task[i];
q->tail = &task[i];
}
else
{
q->tail->next = &task[i];
q->tail = &task[i];
}
}
// show the queue
Task *temp = q->head;
printf("task\tarrive\tneed\tpriority\n");
while (temp != NULL)
{
printf("%d\t%d\t%d\t%d\t\n", temp->taskName, temp->arriveTime, temp->needTime, temp->priority);
temp = temp->next;
}
printf("\n");
// The decision mode: FCFS.
printf("The decision mode: FCFS.\n");
// Deep copy: create scheduling queue copy for FCFS function
Queue *q1 = (Queue *)malloc(sizeof(Queue));
q1->CPUTime = 0;
q1->waitTime = 0;
q1->head = q1->tail = NULL;
temp = q->head;
while (temp != NULL)
{
Task *temp1 = (Task *)malloc(sizeof(Task));
temp1->arriveTime = temp->arriveTime;
temp1->needTime = temp->needTime;
temp1->priority = temp->priority;
temp1->stat = temp->stat;
temp1->taskName = temp->taskName;
temp1->next = NULL;
if (q1->head == NULL)
{
q1->head = temp1;
q1->tail = temp1;
}
else
{
q1->tail->next = temp1;
q1->tail = temp1;
}
temp = temp->next;
}
FCFS(q1);
printf("\n");
// The decision mode: SJF.
printf("The decision mode: SJF.\n");
// Deep copy: create scheduling queue copy for SJF function
Queue *q2 = (Queue *)malloc(sizeof(Queue));
q2->CPUTime = 0;
q2->waitTime = 0;
q2->head = q2->tail = NULL;
temp = q->head;
while (temp != NULL)
{
Task *temp2 = (Task *)malloc(sizeof(Task));
temp2->arriveTime = temp->arriveTime;
temp2->needTime = temp->needTime;
temp2->priority = temp->priority;
temp2->stat = temp->stat;
temp2->taskName = temp->taskName;
temp2->next = NULL;
if (q2->head == NULL)
{
q2->head = temp2;
q2->tail = temp2;
}
else
{
q2->tail->next = temp2;
q2->tail = temp2;
}
temp = temp->next;
}
SJF(q2);
printf("\n");
// The decision mode: Priority.
printf("The decision mode: Priority.\n");
// Deep copy: create scheduling queue copy for Priority function
Queue *q3 = (Queue *)malloc(sizeof(Queue));
q3->CPUTime = 0;
q3->waitTime = 0;
q3->head = q3->tail = NULL;
temp = q->head;
while (temp != NULL)
{
Task *temp3 = (Task *)malloc(sizeof(Task));
temp3->arriveTime = temp->arriveTime;
temp3->needTime = temp->needTime;
temp3->priority = temp->priority;
temp3->stat = temp->stat;
temp3->taskName = temp->taskName;
temp3->next = NULL;
if (q3->head == NULL)
{
q3->head = temp3;
q3->tail = temp3;
}
else
{
q3->tail->next = temp3;
q3->tail = temp3;
}
temp = temp->next;
}
Priority(q3);
printf("\n");
// free array and queue
free(task);
free(q);
return 0;
}
void FCFS(Queue *q)
{
int count = 0; // count of thread
q->CPUTime = 0;
q->waitTime = 0;
// scheduling threads
Task *temp = q->head;
while (temp != NULL)
{
pthread_create(&temp->ptid, NULL, &runner, temp);
sleep(1);
// calculate CPU time and waiting time
if (q->CPUTime - temp->arriveTime > 0) // when this thread comes, CPU is working, so it have to wait
{
q->waitTime += ((q->CPUTime) - (temp->arriveTime));
}
else // when this thread comes, CPU is free, so it doesn's need to wait
{
q->CPUTime = temp->arriveTime;
}
count++;
q->CPUTime += temp->needTime;
pthread_join(temp->ptid, NULL);
Task *freeTemp = temp;
temp = temp->next;
free(freeTemp);
}
printf("Now time is %ds. The average waiting time is %lfs\n", q->CPUTime, 1.0 * q->waitTime / count);
free(q);
return;
}
void SJF(Queue *q)
{
int count = 0; // count of thread
Task *temp;
while (q->head != NULL)
{
temp = q->head;
if (q->CPUTime <= temp->arriveTime) // when this thread comes, CPU is free, so it doesn's need to wait
{
q->CPUTime = temp->arriveTime;
}
else /* when this thread comes, CPU is working.
So when CPU is free, select the thread with shortest job time in the now queue.*/
{
temp = SJFfindNextJob(q);
// move the thread to head
if (temp->taskName != q->head->taskName)
{
Task *preTemp = q->head;
while (preTemp->next->taskName != temp->taskName)
{
preTemp = preTemp->next;
}
preTemp->next = temp->next;
temp->next = q->head;
q->head = temp;
}
q->waitTime += ((q->CPUTime) - (temp->arriveTime));
}
pthread_create(&temp->ptid, NULL, &runner, temp);
pthread_join(temp->ptid, NULL);
q->CPUTime += temp->needTime;
count++;
q->head = temp->next;
free(temp);
sleep(1);
}
printf("Now time is %ds. The average waiting time is %lfs\n", q->CPUTime, 1.0 * q->waitTime / count);
free(q);
}
void Priority(Queue *q)
{
int count = 0; // count of thread
Task *temp;
while (q->head != NULL)
{
temp = q->head;
if (q->CPUTime <= temp->arriveTime) // when this thread comes, CPU is free, so it doesn's need to wait
{
q->CPUTime = temp->arriveTime;
}
else /* when this thread comes, CPU is working.
So when CPU is free, select the thread with highest priority in the now queue.*/
{
temp = PfindNextJob(q);
// move the thread to head
if (temp->taskName != q->head->taskName)
{
Task *preTemp = q->head;
while (preTemp->next->taskName != temp->taskName)
{
preTemp = preTemp->next;
}
preTemp->next = temp->next;
temp->next = q->head;
q->head = temp;
}
q->waitTime += ((q->CPUTime) - (temp->arriveTime));
}
pthread_create(&temp->ptid, NULL, &runner, temp);
pthread_join(temp->ptid, NULL);
q->CPUTime += temp->needTime;
count++;
q->head = temp->next;
free(temp);
sleep(1);
}
printf("Now time is %ds. The average waiting time is %lfs\n", q->CPUTime, 1.0 * q->waitTime / count);
free(q);
}
实验过程
可以看到3种调度策略都成功实现,与预想中的调度结果一致。平均等待时间也与我们计算的一致。
改变变量,再次测试:
仍得到理想的结果。
实验反思
由于对信号量的使用仍不熟练,此次实验中未使用信号量,没有达到实验要求,仍需进一步对程序进行改进。