文章目錄
一、實驗目的
用高級語言編寫和調試一個或多個作業調度的模擬程序,以加深對作業調度算法的理解。因爲源碼中我對一些關鍵步驟的註釋已經比較清晰了,所以在本文中不會再對每一個細節都進行分析,只分析整體的代碼結構和所使用到的設計模式。
博客內所有文章均爲 原創,所有示意圖均爲 原創,若轉載請附原文鏈接。
二、實驗內容
2.1 單道處理系統的作業調度
作業調度算法:分別採用先來先服務(FCFS),最短作業優先(SJF)、響應比高者優先(HRRN)的調度算法。
對每種調度算法都要求打印每個作業開始運行時刻、完成時刻、週轉時間、帶權週轉時間,以及這組作業的平均週轉時間及帶權平均週轉時間,以比較各種算法的優缺點。
2.2 多道程序系統的作業調度
作業調度算法:採用基於先來先服務的調度算法或基於優先級的作業調度算法。
對於多道程序系統,要假定系統中具有的各種資源及數量、調度作業時必須考慮到每個作業的資源要求。
三、流程圖
3.1 單道批處理系統的作業調度
四、設計思想
4.1 設計思路
對於單道批處理系統,由於在單道批處理系統中,作業一投入運行,它就佔有計算機的一切資源直到作業完成爲止,因此調度作業時不必考慮它所需要的資源是否得到滿足,它所佔用的 CPU時限等因素。
作業調度算法:採用先來先服務(FCFS)調度算法,即按作業提交的先後次序進行調度。總是首先調度在系統中等待時間最長的作業。每個作業由一個作業控制塊JCB表示,JCB可以包含如下信息:作業名、提交時間、所需的運行時間、所需的資源、作業狀態、鏈指針等等。
作業的狀態可以是等待W(Wait)、運行R(Run)和完成F(Finish)三種狀態之一。每個作業的最初狀態總是等待W。各個等待的作業按照提交時刻的先後次序排隊,總是首先調度等待隊列中隊首的作業。每個作業完成後要打印該作業的開始運行時刻、完成時刻、週轉時間和帶權週轉時間,這一組作業完成後要計算並打印這組作業的平均週轉時間、帶權平均週轉時間。
而對於多道批處理系統來說,作業調度(響應比)按一定的算法從磁盤上的“輸入井”中選擇資源能得到滿足的作業裝入內存,使作業有機會去佔用處理器執行。但是,一個作業能否佔用處理器,什麼時間能夠佔用處理器,必須由進程調度來決定。所以,作業調度選中 了一個作業且把它裝入內存時,就應爲該作業創建一個進程,若有多個作業被裝入內存,則內存中同時存在多個進程,這些進程的初始狀態爲就緒狀態,然後,由進程調度(優先數)來選擇當前可佔用處理器的進程,進程運行中由於某種原因狀態發生變化,當它讓出處理器時,進程調度就再選另一個作業的進程運行。 因此,作業調度與進程調度相互配合才能實現多道作業的並行執行。
4.2 單道批處理系統
4.2.1 代碼結構
因爲這裏的單道批處理系統需要實現多種算法,因此使用了 模板方法設計模式 + 策略模式 來進行開發,下面爲各種算法的類簽名,算法的具體實現可見代碼實現。
/* 單道批處理系統基類 */
class SchedulingAlgorithm;
/* First-Come First-Served 先來先服務算法 */
class FCFSSchedulingAlgorithm : public SchedulingAlgorithm;
/* Short Job First 短作業優先算法 */
class SJFSchedulingAlgorithm : public SchedulingAlgorithm;
/* Highest Response Ratio Next 高響應比優先算法 */
class HRRNSchedulingAlgorithm : public SchedulingAlgorithm;
4.2.2 FCFS
因爲這裏使用優先級隊列來實現,所以我們只需要爲每種算法定義不同的比較函數即可,比如對於FCFS算法來說,它的比較算法就是比較該作業提交的時間。
// First-Come First-Served
class FCFSSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
bool operator()(const JCB j1, const JCB j2)
{
return j1.submit_time > j2.submit_time;
}
};
FCFSSchedulingAlgorithm(){}
};
4.2.3 SJF
對於SJF算法來說,它的比較函數就是比較作業的時間長度(時間越短優先級越高)。
// Short Job First
class SJFSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
bool operator()(const JCB j1, const JCB j2)
{
return j1.required_running_time > j2.required_running_time;
}
};
SJFSchedulingAlgorithm(){}
};
4.2.4 HRRN
該算法的比較函數比較的是每個作業的響應比:(等待時間+要求服務時間)/要求服務時間,響應比越高,優先級越高,越優先被調度。
// Highest Response Ratio Next
class HRRNSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
double getPriority(JCB jcb)
{
return ((cur_time - jcb.submit_time) + jcb.required_running_time) / jcb.required_running_time;
}
bool operator()(const JCB j1, const JCB j2)
{
return getPriority(j1) > getPriority(j2);
}
};
HRRNSchedulingAlgorithm(){}
};
4.3 多道批處理系統
4.3.1 FCFS + PSA
對於多道批處理系統來說,情況會複雜一些,因爲對於多道批處理系統,除了作業的調度還存在進程的調度,也就是作業調度決定該任務能不能使用處理機(有沒有資格),但就算該任務被調度了,因爲多道批處理系統,還需要考慮相關的資源,因此能不能使用到處理機,還要看進程的調度。
因此在這裏對於作業調度採用了FCFS算法,而對於進程的調度採用了PSA算法(靜態優先級,可搶佔),並且沒有繼續使用實驗一中的進程調度算法的代碼,而是重新寫了一遍。
所有的任務初始會被存放在 back_queue 中,而就緒的任務和正在運行的任務都存儲於 psa_ready_queue 中,等到 psa_ready_queue 中存在空間時,會通過任務調度算法從 back_queue 中選擇合適的任務進行調度,當任務(準確說是進程)被調到 psa_ready_queue 中後,會根據它的優先級判斷它是否能夠優先運行,如果不能就只能保持就緒狀態,一直等到優先級高的進程運行完畢後再運行。
因爲這部分代碼的邏輯比較複雜,所以就不在這裏單獨羅列了,具體可見下面的代碼實現。需要注意的是多道批處理系統代碼邏輯中被註釋掉的代碼爲測試代碼,可以使用其來對代碼的準確性進行測試。
五、代碼實現
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <iomanip>
typedef int TimeSlice;
typedef int Resource;
typedef std::queue<TimeSlice> * JobTimeSeries;
TimeSlice cur_time = 0;
int average_turnaround_time = 0;
double average_power_turnaround_time = 0;
enum ProcessStatus
{
Ready = 0,
Running = 1,
//Finish = 2
};
typedef int Pid;
typedef int TimeSlice;
typedef int Priority;
struct JCB;
typedef JCB * JCBptr;
struct PCB
{
PCB(JCBptr jcb_) : jcb(jcb_){}
Pid pid;
ProcessStatus status;
Priority priority;
JCBptr jcb;
};
typedef PCB * PCBptr;
enum JobStatus
{
Wait = 0,
Run = 1,
Finish = 2
};
struct JCB
{
std::string job_name;
TimeSlice submit_time;
TimeSlice required_running_time;
TimeSlice start_time = -1;
TimeSlice finish_time = 0;
TimeSlice already_run_time = 0;
Resource required_resource;
JobStatus job_status;
PCBptr pcb = new PCB(this);
};
class Util
{
public:
static inline int getRandom(const int min_val, const int max_val, int match)
{
srand(time(0) + match);
return rand() % (max_val - min_val - 1) + min_val;
}
static inline bool printJobData(const JCB jcb)
{
TimeSlice start_time = cur_time;
TimeSlice finish_time = start_time + jcb.required_running_time;
TimeSlice turnaround_time = finish_time - jcb.submit_time;
double power_turnaround_time = turnaround_time / (jcb.required_running_time / 1.0);
std::cout << "[Data] " << jcb.job_name << " "
<< " Start time: " << start_time << " "
<< " Finish time: " << finish_time << " "
<< " Turnaround time: " << turnaround_time << " "
<< " Power turnaround time: " << std::setprecision(2) << power_turnaround_time << std::endl;
average_turnaround_time += turnaround_time;
average_power_turnaround_time += power_turnaround_time;
return true;
}
static inline bool printJobData(const JCB jcb, bool isMul)
{
TimeSlice turnaround_time = jcb.finish_time - jcb.submit_time;
double power_turnaround_time = turnaround_time / (jcb.required_running_time / 1.0);
std::cout << "[Data] " << jcb.job_name << " "
<< " Finish time: " << jcb.finish_time << " "
<< " Turnaround time: " << turnaround_time << " "
<< " Power turnaround time: " << std::setprecision(2) << power_turnaround_time << std::endl;
average_turnaround_time += turnaround_time;
average_power_turnaround_time += power_turnaround_time;
return true;
}
};
class SchedulingAlgorithm
{
public:
// virtual bool initPCB(std::priority_queue<JCB, std::vector<JCB>, std::less<JCB>> * ready_queue, int process_num) = 0;
// virtual bool processPCB(JCB & pcb) = 0;
};
// First-Come First-Served
class FCFSSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
bool operator()(const JCB j1, const JCB j2)
{
return j1.submit_time > j2.submit_time;
}
};
FCFSSchedulingAlgorithm()
{}
private:
};
// Short Job First
class SJFSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
bool operator()(const JCB j1, const JCB j2)
{
return j1.required_running_time > j2.required_running_time;
}
};
SJFSchedulingAlgorithm()
{}
};
// Highest Response Ratio Next
class HRRNSchedulingAlgorithm : public SchedulingAlgorithm
{
public:
struct PriorityCmp
{
double getPriority(JCB jcb)
{
return ((cur_time - jcb.submit_time) + jcb.required_running_time) / jcb.required_running_time;
}
bool operator()(const JCB j1, const JCB j2)
{
return getPriority(j1) > getPriority(j2);
}
};
HRRNSchedulingAlgorithm()
{}
};
template<typename PriorityCmp>
class JobScheduling
{
public:
JobScheduling(int job_num)
{
job_num_ = job_num;
sa_ = new SchedulingAlgorithm();
wait_queue_ = new std::priority_queue<JCB, std::vector<JCB>, PriorityCmp>();
mock_jcbs_ = new std::priority_queue<JCB, std::vector<JCB>, PriorityCmpForMock>();
finish_queue_ = new std::queue<JCB>();
mockJCBs();
}
protected:
struct PriorityCmpForMock
{
bool operator()(const JCB j1, const JCB j2)
{
return j1.submit_time > j2.submit_time;
}
};
bool mockJCBs()
{
for (int i = 0; i < job_num_; ++i)
{
JCBptr jcb = new JCB();
jcb->job_name = "job" + std::to_string(i);
jcb->job_status = Wait;
jcb->submit_time = Util::getRandom(1, 20, i);
// The minimum value is 3 so that each segment can be divided into time slice
jcb->required_running_time = Util::getRandom(3, 10, i);
mock_jcbs_->push(*jcb);
std::cout << "[INFO] finish init " << jcb->job_name << " with "
<< " submit_time " << jcb->submit_time
<< " required_running_time " << jcb->required_running_time << std::endl;
}
}
void getCurrentReadyQueue()
{
while (!wait_queue_->empty())
{
JCB jcb = wait_queue_->top();
std::cout << jcb.submit_time << std::endl;
wait_queue_->pop();
}
}
int job_num_;
SchedulingAlgorithm* sa_;
std::priority_queue<JCB, std::vector<JCB>, PriorityCmpForMock>* mock_jcbs_;
std::priority_queue<JCB, std::vector<JCB>, PriorityCmp>* wait_queue_;
std::queue<JCB>* finish_queue_;
};
template<typename SchedulingAlgorithm, typename PriorityCmp>
class SimpleBatchProcessingSystemJobScheduling : JobScheduling<PriorityCmp>
{
public:
SimpleBatchProcessingSystemJobScheduling(int job_num)
: JobScheduling<PriorityCmp>(job_num)
{}
bool start()
{
cur_time = this->mock_jcbs_->top().submit_time;
while (!this->wait_queue_->empty() || !this->mock_jcbs_->empty())
{
// Simulate adding tasks dynamically
if (!this->mock_jcbs_->empty() && this->mock_jcbs_->top().submit_time <= cur_time)
{
JCB jcb = this->mock_jcbs_->top();
this->mock_jcbs_->pop();
std::cout << "[INFO] add " << jcb.job_name << " to wait queue." << std::endl;
this->wait_queue_->push(jcb);
}
if (!this->wait_queue_->empty())
{
JCB jcb = this->wait_queue_->top();
this->wait_queue_->pop();
std::cout << "[INFO] begin to do " << jcb.job_name << "." << std::endl;
jcb.job_status = Run;
// simulation do job
sleep(1);
std::cout << "[INFO] do " << jcb.job_name << " finish." << std::endl;
jcb.job_status = Finish;
// print job Data.
Util::printJobData(jcb);
this->finish_queue_->push(jcb);
cur_time += jcb.required_running_time;
}
else
{
// Slowly increase time slice when there is no job
cur_time += 1;
}
}
std::cout << "[INFO] all jobs finished." << std::endl;
std::cout << "[LOG] average turnaround time " << (average_turnaround_time / this->job_num_)
<< " average power turnaround time " << (average_power_turnaround_time / this->job_num_) << std::endl;
}
private:
};
class MultiprogrammedBatchProcessingSystemJobScheduling
{
public:
struct PriorityCmpForPCB
{
bool operator()(const PCBptr p1, const PCBptr p2)
{
return p1->priority < p2->priority;
}
};
struct PriorityCmpForBack
{
bool operator()(const JCBptr j1, const JCBptr j2)
{
return j1->submit_time > j2->submit_time;
}
};
MultiprogrammedBatchProcessingSystemJobScheduling()
{
back_queue_ = new std::priority_queue<JCBptr, std::vector<JCBptr>, PriorityCmpForBack>();
psa_ready_queue_ = new std::priority_queue<PCBptr, std::vector<PCBptr>, PriorityCmpForPCB>();
std::cout << "input job num:" << std::endl;
std::cin >> job_num_;
for (int i = 0; i < job_num_; i++)
{
std::cout << "input job" << i << " submit_time & required_running_time & priority" << std::endl;
JCBptr jcb = new JCB();
jcb->job_name = "job" + std::to_string(i);
jcb->job_status = Wait;
std::cin >> jcb->submit_time;
std::cin >> jcb->required_running_time;
std::cin >> jcb->pcb->priority;
back_queue_->push(jcb);
}
/*
job_num_ = 6;
JCBptr jcb = new JCB();
jcb->job_name = "A";
jcb->job_status = Wait;
jcb->submit_time = 0;
jcb->required_running_time = 50;
jcb->pcb->priority = 5;
back_queue_->push(jcb);
jcb = new JCB();
jcb->job_name = "B";
jcb->job_status = Wait;
jcb->submit_time = 20;
jcb->required_running_time = 60;
jcb->pcb->priority = 7;
back_queue_->push(jcb);
jcb = new JCB();
jcb->job_name = "C";
jcb->job_status = Wait;
jcb->submit_time = 50;
jcb->required_running_time = 40;
jcb->pcb->priority = 3;
back_queue_->push(jcb);
jcb = new JCB();
jcb->job_name = "D";
jcb->job_status = Wait;
jcb->submit_time = 80;
jcb->required_running_time = 80;
jcb->pcb->priority = 8;
back_queue_->push(jcb);
jcb = new JCB();
jcb->job_name = "E";
jcb->job_status = Wait;
jcb->submit_time = 100;
jcb->required_running_time = 30;
jcb->pcb->priority = 6;
back_queue_->push(jcb);
jcb = new JCB();
jcb->job_name = "F";
jcb->job_status = Wait;
jcb->submit_time = 120;
jcb->required_running_time = 70;
jcb->pcb->priority = 9;
back_queue_->push(jcb);
*/
}
bool start()
{
cur_time = back_queue_->top()->submit_time;
while (!back_queue_->empty() || !psa_ready_queue_->empty())
{
bool increase_time = true;
// Job FCFS
if(!back_queue_->empty() && psa_ready_queue_->size() < 2)
{
JCBptr jcb = back_queue_->top();
if (jcb->submit_time <= cur_time)
{
back_queue_->pop();
// Process PSA
if (!psa_ready_queue_->empty())
{
PCBptr pcb = psa_ready_queue_->top();
JCBptr jcb = pcb->jcb;
if (jcb->already_run_time >= jcb->required_running_time)
{
jcb->job_status = Finish;
psa_ready_queue_->pop();
//std::cout << "[LOG]" << jcb->job_name << " Finish At " << cur_time << std::endl;
jcb->finish_time = cur_time;
Util::printJobData(*jcb, true);
}
else
{
jcb->job_status = Wait;
pcb->status = Ready;
//std::cout << "[LOG]" << jcb->job_name << " Ready At " << cur_time << std::endl;
}
}
psa_ready_queue_->push(jcb->pcb);
PCBptr pcb = psa_ready_queue_->top();
JCBptr jcb = pcb->jcb;
jcb->job_status = Run;
pcb->status = Running;
//jcb->start_time = (jcb->start_time == -1) ? cur_time : jcb->start_time;
//std::cout << "[LOG]" << jcb->job_name << " Begin Running At " << cur_time << std::endl;
increase_time = false;
}
}
else if (!psa_ready_queue_->empty())
{
PCBptr pcb = psa_ready_queue_->top();
JCBptr jcb = pcb->jcb;
if (jcb->already_run_time >= jcb->required_running_time)
{
jcb->job_status = Finish;
psa_ready_queue_->pop();
//std::cout << "[LOG]" << jcb->job_name << " Finish At " << cur_time << std::endl;
jcb->finish_time = cur_time;
increase_time = false;
JCBptr jtop = psa_ready_queue_->top()->jcb;
//jtop->start_time = (jtop->start_time == -1) ? cur_time : jtop->start_time;
Util::printJobData(*jcb, true);
}
}
if (increase_time)
{
if (!psa_ready_queue_->empty())
{
PCBptr pcb = psa_ready_queue_->top();
pcb->jcb->already_run_time++;
}
cur_time++;
}
}
std::cout << "[INFO] all jobs finished." << std::endl;
std::cout << "[LOG] average turnaround time " << (average_turnaround_time / job_num_)
<< " average power turnaround time " << (average_power_turnaround_time / job_num_) << std::endl;
}
private:
int job_num_;
std::priority_queue<JCBptr, std::vector<JCBptr>, PriorityCmpForBack>* back_queue_;
std::priority_queue<PCBptr, std::vector<PCBptr>, PriorityCmpForPCB>* psa_ready_queue_;
};
/*
int main()
{
SimpleBatchProcessingSystemJobScheduling<FCFSSchedulingAlgorithm> js(4);
js.getCurrentReadyQueue();
// pause to see result
getchar();
return 0;
}
*/
六、運行結果
6.1 單道批處理(FCFS)
6.2 單道批處理(SJF)
6.3 單道批處理(HRRN)
6.4 多道批處理(FCFS + PSA)
七、結尾
如果本文描述的內容或使用的代碼存在任何問題,請及時聯繫我或在本篇文章的下面進行評論,我會本着對每一位學技術同學的負責態度立即修改。在後續還會有三篇計算機操作系統的算法 C++ 復現博文,如果感興趣可以關注我。