組長:B10415016、楊璿譯
組員:B10415039、曾柏誠
github: https://github.com/BryantTseng/xv6-public
實作scheduling功能,共包含三種演算法:
以下將針對此三種方法作使用情境之說明。
當第一個process到來時,將先執行其需求,若同時有許多process可被服務(runnable),則選擇抵達時間最早的process執行,故其便無starvation的問題,因為每個process都將被執行到,但若先抵達的process,須執行時間較長,則會導致平均等待時間變長。
scheduler將會給與每個process一個優先度,其會挑選優先度最高的先執行,執行完後挑次高的執行,若同時有優先度相同的process,則選擇到達時間低的執行。但此種方法便有可能發生satarvation,因為高優先度的process若可能被高頻率的呼叫,將會導致低優先度的process無法被執行,故會發生starvation。而我們解決方式為,process在完成任務便將priority-1,而同時若有process之優先度為1者則+1,這樣便可解決starvation的問題
Scheduler將會定一段執行時間,規定每個process使用cpu資源時的時間長短,當規定時間一到便直接換下一個process使用,故不會有starvation的問題,雖然如此,但訂定執行時間須注意一些問題
透過 GeneralTest 指令模擬出 CPU-bound 以及 I/O-bound 的process
然後分別用 Default(RR)、FCFS、Priority 三種不同的排程演算法執行
並觀察在不同的scheduler演算法下process的執行順序為何?


































defs.h
void scheduler(void) __attribute__((noreturn));
int set_prio(int);
proc.c
static struct proc* allocproc(void)
{
struct proc *p;
char *sp;
acquire(&ptable.lock);
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == UNUSED){
break;
}
}
if(p->state!=UNUSED){
release(&ptable.lock);
return 0;
}
p->state = INIT;
p->pid = nextpid++;
p->ctime = ticks;
p->retime = 0;
p->rutime = 0;
p->stime = 0;
p->fake[0] = '*';
p->fake[1] = '*';
p->fake[2] = '*';
p->fake[3] = '*';
p->fake[4] = '*';
p->fake[5] = '*';
p->fake[6] = '*';
p->fake[7] = '*';
release(&ptable.lock);
// Allocate kernel stack.
if((p->kstack = kalloc()) == 0){
p->state = UNUSED;
return 0;
}
sp = p->kstack + KSTACKSIZE;
// Leave room for trap frame.
sp -= sizeof *p->tf;
p->tf = (struct trapframe*)sp;
// Set up new context to start executing at forkret,
// which returns to trapret.
sp -= 4;
*(uint*)sp = (uint)trapret;
sp -= sizeof *p->context;
p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context);
p->context->eip = (uint)forkret;
cprintf("allocate, pid: %d, creation time: %d, process name: %s, now time: %d\n\n", p->pid, p->ctime, p->name, ticks);
return p;
}
#ifdef SML
/*
this method will find the next process to run
*/
struct proc* findReadyProcess(int *index1, int *index2, int *index3, uint *wantedPriority) {
int i;
struct proc* newProc;
while(1){
for (i = 0; i < NPROC; i++) {
switch(*wantedPriority) {
case 1:
newProc = &ptable.proc[(*index1 + i) % NPROC];
if (newProc->state == RUNNABLE && newProc->priority == *wantedPriority) {
*index1 = (*index1 + 1 + i) % NPROC;
return newProc; // found a runnable process with appropriate priority
}
case 2:
newProc = &ptable.proc[(*index2 + i) % NPROC];
if (newProc->state == RUNNABLE && newProc->priority == *wantedPriority) {
*index2 = (*index2 + 1 + i) % NPROC;
return newProc; // found a runnable process with appropriate priority
}
case 3:
newProc = &ptable.proc[(*index3 + i) % NPROC];
if (newProc->state == RUNNABLE && newProc->priority == *wantedPriority){
*index3 = (*index3 + 1 + i) % NPROC;
return newProc; // found a runnable process with appropriate priority
}
}
}
if (*wantedPriority == 1) {//did not find any process on any of the prorities
*wantedPriority = 3;
return 0;
}
else {
*wantedPriority -= 1; //will try to find a process at a lower priority
continue;
}
}
return 0;
}
#endif
void scheduler(void)
{
struct proc *p;
int index1 = 0;
int index2 = 0;
int index3 = 0;
int x = index1;
index1 = x;
x = index2;
index2 = x;
x = index3;
index3 = x;
for(;;){
// Enable interrupts on this processor.
sti();
// Loop over process table looking for process to run.
acquire(&ptable.lock);
// the differnt options for scheduling policies, chosen during compilation
#ifdef DEFAULT//default scheduler round robin
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state != RUNNABLE)
continue;
// Switch to chosen process. It is the process's job
// to release ptable.lock and then reacquire it
// before jumping back to us.
proc = p;
switchuvm(p);
//cprintf("RR, pid: %d, creation time: %d, process name: %s, now time: %d\n\n", p->pid, p->ctime, p->name, ticks);
p->state = RUNNING;
cprintf("==============================================\n");
procdump();
cprintf("==============================================\n");
//cprintf("\n\n");
//procdump();
// cprintf("\n\n");
swtch(&cpu->scheduler, proc->context);
switchkvm();
// cprintf("%d+%s\n",p->pid,p->name);
// Process is done running for now.
// It should have changed its p->state before coming back.
proc = 0;
}
#else
#ifdef FCFS //Scheduler select FCFS
struct proc *minP = NULL;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == RUNNABLE){//check if state is runnable
if (minP!=NULL){//not a null process
if(p->ctime < minP->ctime)//find the earlist createtime process
minP = p;
}
else
minP = p;//first time in loop, set to minp
}
}
if (minP!=NULL){
p = minP;//the process with the smallest creation time
proc = p;
switchuvm(p);
p->state = RUNNING;
cprintf("==============================================\n");
procdump();
cprintf("==============================================\n");
swtch(&cpu->scheduler, proc->context);
switchkvm();
// Process is done running for now.
// It should have changed its p->state before coming back.
proc = 0;
}
#else
#ifdef SML//Scheduler select priority
uint priority = 3;
p = findReadyProcess(&index1, &index2, &index3, &priority);
if (p == 0) {
release(&ptable.lock);
continue;
}
proc = p;
switchuvm(p);
p->state = RUNNING;
cprintf("==============================================\n");
procdump();
cprintf("==============================================\n");
swtch(&cpu->scheduler, proc->context);
switchkvm();
// Deal the starvation problem
if(p->priority > 2)// let the priority of running process minus 1
p->priority--;
// let the priority of non-running process minus 1
struct proc *temp;
for(temp = ptable.proc; temp < &ptable.proc[NPROC]; temp++)
{
if(temp->pid != p->pid && temp->priority < 3){
temp->priority++;//add other process's priority
}
}
proc = 0;
#endif
#endif
#endif
release(&ptable.lock);
}
}
void updateRecord() {
struct proc *p;
acquire(&ptable.lock);
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
switch(p->state) {
case SLEEPING:
p->stime++;
break;
case RUNNABLE:
p->retime++;
break;
case RUNNING:
p->rutime++;
break;
default:
;
}
}
release(&ptable.lock);
}
int set_prio(int priority) {
if (priority < 1 || priority > 3)
return -1;
acquire(&ptable.lock);
proc->priority = priority;
release(&ptable.lock);
return 0;
}
void decpriority(void) {
// acquire(&ptable.lock);
proc->priority = proc->priority == 1 ? 1 : proc->priority - 1;
// release(&ptable.lock);
}
proc.h
struct proc {
char name[16]; // Process name (debugging)
int pid; // Process ID
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall
struct context *context; // swtch() here to run process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
uint ctime; // Process creation time
int stime; //process SLEEPING time
int retime; //process READY(RUNNABLE) time
int rutime; //process RUNNING time
int priority;
int tickcounter;
char fake[8];
};
GeneralTest.c
int
main(int argc, char *argv[])
{
if (argc != 3){
printf(1, "Usage: GeneralTest <n> <SchedulerType>\n");
exit();
}
#ifndef SML
int i;
int n;
int j = 0;
int k;
int retime;
int rutime;
int stime;
int sums[2][3];
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
sums[i][j] = 0;
n = atoi(argv[1]);
i = n; //unimportant
int pid;
for (i = 0; i < 2 * n; i++) {
j = i % 2;
pid = fork();
if (pid == 0) {//child
j = (getpid() - 4) % 2; // ensures independence from the first son's pid when gathering the results in the second part of the program
switch(j) {
case 0: //CPU bound process (CPU):
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
case 1:// simulate I/O bound process (IO)
for(k = 0; k < 1; k++){
sleep(1);
}
break;
}
exit(); // children exit here
}
continue; // father continues to spawn the next child
}
for (i = 0; i < 2 * n; i++) {
pid = wait2(&retime, &rutime, &stime);
int res = (pid) % 2; // correlates to j in the dispatching loop
switch(res) {
case 0: // CPU bound processes
printf(1, "CPU-bound, pid: %d, ready: %d, running: %d, sleeping: %d, turnaround: %d\n", pid, retime, rutime, stime, retime + rutime + stime);
sums[0][0] += retime;
sums[0][1] += rutime;
sums[0][2] += stime;
break;
case 1: // simulating I/O bound processes
printf(1, "I/O-bound, pid: %d, ready: %d, running: %d, sleeping: %d, turnaround: %d\n", pid, retime, rutime, stime, retime + rutime + stime);
sums[1][0] += retime;
sums[1][1] += rutime;
sums[1][2] += stime;
break;
}
}
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
sums[i][j] /= n;
printf(1, "\n\nCPU bound:\nAverage ready time: %d\nAverage running time: %d\nAverage sleeping time: %d\nAverage turnaround time: %d\n\n\n", sums[0][0], sums[0][1], sums[0][2], sums[0][0] + sums[0][1] + sums[0][2]);
printf(1, "\n\nI/O bound:\nAverage ready time: %d\nAverage running time: %d\nAverage sleeping time: %d\nAverage turnaround time: %d\n\n\n", sums[1][0], sums[1][1], sums[1][2], sums[1][0] + sums[1][1] + sums[1][2]);
#endif
#ifdef SML
int i;
int n;
int j = 0;
int k;
int retime;
int rutime;
int stime;
int sums[3][3];
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
sums[i][j] = 0;
n = atoi(argv[1]);
i = n; //unimportant
int pid;
for (i = 0; i < n; i++) {
j = i % 3;
pid = fork();
if (pid == 0) {//child
j = (getpid() - 4) % 3; // ensures independence from the first son's pid when gathering the results in the second part of the program
#ifdef SML
switch(j) {
case 0:
set_prio(1);
break;
case 1:
set_prio(2);
break;
case 2:
set_prio(3);
break;
}
#endif
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
for (k = 0; k < 100; k++){
for (j = 0; j < 1000000; j++){}
}
exit(); // children exit here
}
continue; // father continues to spawn the next child
}
for (i = 0; i < 3 * n; i++) {
pid = wait2(&retime, &rutime, &stime);
int res = (pid - 4) % 3; // correlates to j in the dispatching loop
switch(res) {
case 0: // CPU bound processes
printf(1, "Priority 1, pid: %d, ready: %d, running: %d, sleeping: %d, turnaround: %d\n", pid, retime, rutime, stime, retime + rutime + stime);
sums[0][0] += retime;
sums[0][1] += rutime;
sums[0][2] += stime;
break;
case 1: // CPU bound processes, short tasks
printf(1, "Priority 2, pid: %d, ready: %d, running: %d, sleeping: %d, turnaround: %d\n", pid, retime, rutime, stime, retime + rutime + stime);
sums[1][0] += retime;
sums[1][1] += rutime;
sums[1][2] += stime;
break;
case 2: // simulating I/O bound processes
printf(1, "Priority 3, pid: %d, ready: %d, running: %d, sleeping: %d, turnaround: %d\n", pid, retime, rutime, stime, retime + rutime + stime);
sums[2][0] += retime;
sums[2][1] += rutime;
sums[2][2] += stime;
break;
}
}
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
sums[i][j] /= n;
printf(1, "\n\nPriority 1:\nAverage ready time: %d\nAverage running time: %d\nAverage sleeping time: %d\nAverage turnaround time: %d\n\n\n", sums[0][0], sums[0][1], sums[0][2], sums[0][0] + sums[0][1] + sums[0][2]);
printf(1, "Priority 2:\nAverage ready time: %d\nAverage running time: %d\nAverage sleeping time: %d\nAverage turnaround time: %d\n\n\n", sums[1][0], sums[1][1], sums[1][2], sums[1][0] + sums[1][1] + sums[1][2]);
printf(1, "Priority 3:\nAverage ready time: %d\nAverage running time: %d\nAverage sleeping time: %d\nAverage turnaround time: %d\n\n\n", sums[2][0], sums[2][1], sums[2][2], sums[2][0] + sums[2][1] + sums[2][2]);
#endif
exit();
}
sh.c
printf(1, "Selected scheduling policy: ");
#ifdef DEFAULT
printf(1, "Round Robin\n");
#else
#ifdef FCFS
printf(1, "FCFS\n");
#else
#ifdef SML
printf(1, "Priority\n");
#endif
#endif
#endif
syscall.c
extern int sys_set_prio(void);
syscall.h
#define SYS_set_prio 24
makefile
UPROGS=\
_cat\
_echo\
_forktest\
_grep\
_init\
_kill\
_ln\
_ls\
_mkdir\
_rm\
_sh\
_stressfs\
_usertests\
_wc\
_zombie\
_GeneralTest\
_PriorityTest\
ifndef CPUS
CPUS := 1
endif
sysproc.c
int sys_set_prio(void) {
int priority;
argint(0, &priority);
return set_prio(priority);
}
B10415016 楊璿譯
B10415039 曾柏誠