diff --git a/Makefile b/Makefile index 09d790cf63..d90e7b7170 100644 --- a/Makefile +++ b/Makefile @@ -180,6 +180,7 @@ UPROGS=\ _stressfs\ _usertests\ _wc\ + _sanity\ _zombie\ fs.img: mkfs README $(UPROGS) @@ -249,7 +250,7 @@ qemu-nox-gdb: fs.img xv6.img .gdbinit EXTRA=\ mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\ - ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\ + ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c sanity.c zombie.c\ printf.c umalloc.c\ README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\ .gdbinit.tmpl gdbutil\ diff --git a/defs.h b/defs.h index 82fb982837..fff1a16767 100644 --- a/defs.h +++ b/defs.h @@ -120,6 +120,22 @@ void userinit(void); int wait(void); void wakeup(void*); void yield(void); +void clock(void); +int wait2(int* retime, int* rutime, int* stime); +int set_prio(int); + + +//mudanças aqui +void clock(void); +int wait2(int* retime, int* rutime, int* stime); +int set_prio(int); +int user_yield(); +void update_counters(void); + +void clock(void); +int wait2(int* retime, int* rutime, int* stime); +int set_prio(int); + // swtch.S void swtch(struct context**, struct context*); diff --git a/param.h b/param.h index a7e90efff4..a79b3ebdb7 100644 --- a/param.h +++ b/param.h @@ -10,5 +10,8 @@ #define MAXOPBLOCKS 10 // max # of blocks any FS op writes #define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log #define NBUF (MAXOPBLOCKS*3) // size of disk block cache -#define FSSIZE 1000 // size of file system in blocks - +#define FSSIZE 1000 // size of file system in blocks +#define INTERV 5 // time slice in clock cycles +#define P1TO2 200 // time waiting before promotion from priority 1 to 2 +#define P2TO3 100 // time waiting before promotion from priority 2 to 3 +#define P3TO4 50 // time waiting before promotion from priority 3 to 4 diff --git a/proc.c b/proc.c index 806b1b184b..c0082ed3da 100644 --- a/proc.c +++ b/proc.c @@ -1,12 +1,13 @@ + #include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" -#include "x86.h" #include "proc.h" #include "spinlock.h" + struct { struct spinlock lock; struct proc proc[NPROC]; @@ -26,22 +27,19 @@ pinit(void) initlock(&ptable.lock, "ptable"); } -// Must be called with interrupts disabled int cpuid() { - return mycpu()-cpus; + return mycpu() - cpus; } -// Must be called with interrupts disabled to avoid the caller being -// rescheduled between reading lapicid and running through the loop. struct cpu* mycpu(void) { int apicid, i; - - if(readeflags()&FL_IF) + + if (readeflags() & FL_IF) panic("mycpu called with interrupts enabled\n"); - + apicid = lapicid(); // APIC IDs are not guaranteed to be contiguous. Maybe we should have // a reverse map, or reserve a register to store &cpus[i]. @@ -52,8 +50,6 @@ mycpu(void) panic("unknown apicid\n"); } -// Disable interrupts so that we are not rescheduled -// while reading proc from the cpu structure struct proc* myproc(void) { struct cpu *c; @@ -65,11 +61,6 @@ myproc(void) { return p; } -//PAGEBREAK: 32 -// Look in the process table for an UNUSED proc. -// If found, change state to EMBRYO and initialize -// state required to run in the kernel. -// Otherwise return 0. static struct proc* allocproc(void) { @@ -88,22 +79,24 @@ allocproc(void) found: p->state = EMBRYO; p->pid = nextpid++; + p->timeslice_tracker = 0; + p->ctime = ticks; + p->stime = 0; + p->retime = 0; + p->rutime = 0; + p->priority = 2; 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; @@ -115,8 +108,6 @@ allocproc(void) return p; } -//PAGEBREAK: 32 -// Set up first user process. void userinit(void) { @@ -142,10 +133,6 @@ userinit(void) safestrcpy(p->name, "initcode", sizeof(p->name)); p->cwd = namei("/"); - // this assignment to p->state lets other cores - // run this process. the acquire forces the above - // writes to be visible, and the lock is also needed - // because the assignment might not be atomic. acquire(&ptable.lock); p->state = RUNNABLE; @@ -153,8 +140,6 @@ userinit(void) release(&ptable.lock); } -// Grow current process's memory by n bytes. -// Return 0 on success, -1 on failure. int growproc(int n) { @@ -174,9 +159,6 @@ growproc(int n) return 0; } -// Create a new process copying p as the parent. -// Sets up stack to return as if from system call. -// Caller must set state of returned proc to RUNNABLE. int fork(void) { @@ -184,12 +166,10 @@ fork(void) struct proc *np; struct proc *curproc = myproc(); - // Allocate process. if((np = allocproc()) == 0){ return -1; } - // Copy process state from proc. if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){ kfree(np->kstack); np->kstack = 0; @@ -200,7 +180,6 @@ fork(void) np->parent = curproc; *np->tf = *curproc->tf; - // Clear %eax so that fork returns 0 in the child. np->tf->eax = 0; for(i = 0; i < NOFILE; i++) @@ -221,9 +200,6 @@ fork(void) return pid; } -// Exit the current process. Does not return. -// An exited process remains in the zombie state -// until its parent calls wait() to find out it exited. void exit(void) { @@ -234,7 +210,6 @@ exit(void) if(curproc == initproc) panic("init exiting"); - // Close all open files. for(fd = 0; fd < NOFILE; fd++){ if(curproc->ofile[fd]){ fileclose(curproc->ofile[fd]); @@ -249,10 +224,8 @@ exit(void) acquire(&ptable.lock); - // Parent might be sleeping in wait(). wakeup1(curproc->parent); - // Pass abandoned children to init. for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->parent == curproc){ p->parent = initproc; @@ -261,14 +234,11 @@ exit(void) } } - // Jump into the scheduler, never to return. curproc->state = ZOMBIE; sched(); panic("zombie exit"); } -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. int wait(void) { @@ -278,14 +248,12 @@ wait(void) acquire(&ptable.lock); for(;;){ - // Scan through table looking for exited children. havekids = 0; for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->parent != curproc) continue; havekids = 1; if(p->state == ZOMBIE){ - // Found one. pid = p->pid; kfree(p->kstack); p->kstack = 0; @@ -300,45 +268,47 @@ wait(void) } } - // No point waiting if we don't have any children. if(!havekids || curproc->killed){ release(&ptable.lock); return -1; } - // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(curproc, &ptable.lock); //DOC: wait-sleep + sleep(curproc, &ptable.lock); } } -//PAGEBREAK: 42 -// Per-CPU process scheduler. -// Each CPU calls scheduler() after setting itself up. -// Scheduler never returns. It loops, doing: -// - choose a process to run -// - swtch to start running that process -// - eventually that process transfers control -// via swtch back to the scheduler. void scheduler(void) { struct proc *p; struct cpu *c = mycpu(); c->proc = 0; - + for(;;){ - // Enable interrupts on this processor. sti(); - - // Loop over process table looking for process to run. acquire(&ptable.lock); + + // Implementação do escalonamento 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. + // Reduz a prioridade dos processos que já utilizaram todo o seu timeslice + if (p->timeslice_tracker >= INTERV) { + p->priority++; + p->timeslice_tracker = 0; + } + + // Insere o processo na fila de acordo com sua prioridade + insert_ordered(p); + + p->timeslice_tracker++; + } + + // Seleciona o próximo processo a ser executado + p = nextproc(); + + if(p != 0){ c->proc = p; switchuvm(p); p->state = RUNNING; @@ -346,79 +316,65 @@ scheduler(void) swtch(&(c->scheduler), p->context); switchkvm(); - // Process is done running for now. - // It should have changed its p->state before coming back. c->proc = 0; } - release(&ptable.lock); + release(&ptable.lock); } } -// Enter scheduler. Must hold only ptable.lock -// and have changed proc->state. Saves and restores -// intena because intena is a property of this -// kernel thread, not this CPU. It should -// be proc->intena and proc->ncli, but that would -// break in the few places where a lock is held but -// there's no process. +// Insere um processo na fila de acordo com a ordem de cada política de escalonamento void -sched(void) +insert_ordered(struct proc *p) { - int intena; - struct proc *p = myproc(); + struct proc *q; + int inserted = 0; + + // Inserção ordenada + for(q = ptable.proc; q < &ptable.proc[NPROC]; q++){ + if(q->state != RUNNABLE || q->priority < p->priority){ + memmove(q + 1, q, (int)(&ptable.proc[NPROC] - q) * sizeof(struct proc)); + *q = *p; + inserted = 1; + break; + } + } - if(!holding(&ptable.lock)) - panic("sched ptable.lock"); - if(mycpu()->ncli != 1) - panic("sched locks"); - if(p->state == RUNNING) - panic("sched running"); - if(readeflags()&FL_IF) - panic("sched interruptible"); - intena = mycpu()->intena; - swtch(&p->context, mycpu()->scheduler); - mycpu()->intena = intena; + if (!inserted) { + *(ptable.proc + NPROC - 1) = *p; + } } -// Give up the CPU for one scheduling round. -void -yield(void) +// Retorna o próximo processo a ser executado +struct proc* +nextproc(void) { - acquire(&ptable.lock); //DOC: yieldlock - myproc()->state = RUNNABLE; - sched(); - release(&ptable.lock); + struct proc *p; + + // Seleciona o primeiro processo da fila (FIFO) + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state == RUNNABLE){ + return p; + } + } + + return 0; } -// A fork child's very first scheduling by scheduler() -// will swtch here. "Return" to user space. void -forkret(void) +yield(void) { - static int first = 1; - // Still holding ptable.lock from scheduler. + acquire(&ptable.lock); + myproc()->state = RUNNABLE; + sched(); release(&ptable.lock); - - if (first) { - // Some initialization functions must be run in the context - // of a regular process (e.g., they call sleep), and thus cannot - // be run from main(). - first = 0; - iinit(ROOTDEV); - initlog(ROOTDEV); - } - - // Return to "caller", actually trapret (see allocproc). } -// Atomically release lock and sleep on chan. -// Reacquires lock when awakened. void sleep(void *chan, struct spinlock *lk) { struct proc *p = myproc(); - + if(p == 0) panic("sleep"); @@ -429,8 +385,8 @@ sleep(void *chan, struct spinlock *lk) // change p->state and then call sched. // Once we hold ptable.lock, we can be // guaranteed that we won't miss any wakeup - // (wakeup runs with ptable.lock locked), - // so it's okay to release lk. + // (wakeup locks ptable.lock), so it's okay + // to release lk. if(lk != &ptable.lock){ //DOC: sleeplock0 acquire(&ptable.lock); //DOC: sleeplock1 release(lk); @@ -451,32 +407,29 @@ sleep(void *chan, struct spinlock *lk) } } -//PAGEBREAK! -// Wake up all processes sleeping on chan. -// The ptable lock must be held. -static void -wakeup1(void *chan) +void +wakeup(void *chan) { struct proc *p; + acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) if(p->state == SLEEPING && p->chan == chan) p->state = RUNNABLE; + release(&ptable.lock); } -// Wake up all processes sleeping on chan. void -wakeup(void *chan) +wakeup1(void *chan) { - acquire(&ptable.lock); - wakeup1(chan); - release(&ptable.lock); + struct proc *p; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) + if(p->state == SLEEPING && p->chan == chan) + p->state = RUNNABLE; } -// Kill the process with the given pid. -// Process won't exit until it returns -// to user space (see trap in trap.c). -int +void kill(int pid) { struct proc *p; @@ -485,21 +438,15 @@ kill(int pid) for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->pid == pid){ p->killed = 1; - // Wake process from sleep if necessary. if(p->state == SLEEPING) p->state = RUNNABLE; release(&ptable.lock); - return 0; + return; } } release(&ptable.lock); - return -1; } -//PAGEBREAK: 36 -// Print a process listing to console. For debugging. -// Runs when user types ^P on console. -// No lock to avoid wedging a stuck machine further. void procdump(void) { @@ -532,3 +479,122 @@ procdump(void) cprintf("\n"); } } + +void +clock(void) +{ + struct proc *p; + acquire(&ptable.lock); + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + switch(p->state){ + case RUNNABLE: + p->retime++; + break; + case RUNNING: + p->rutime++; + break; + case SLEEPING: + p->stime++; + break; + } + } + + release(&ptable.lock); +} + +// Retorna o tempo total do processo p (retime + rutime + stime) +int +get_total_time(struct proc *p) +{ + return p->retime + p->rutime + p->stime; +} + +// Retorna o tempo de turnaround do processo p (tempo total - tempo de criação) +int +get_turnaround_time(struct proc *p) +{ + return get_total_time(p) - (ticks - p->ctime); +} + +// Retorna o tempo de espera do processo p (tempo total - tempo de execução - tempo de criação) +int +get_waiting_time(struct proc *p) +{ + return get_turnaround_time(p) - p->rutime; +} + +// Retorna o tempo de resposta do processo p (tempo de execução - tempo de criação) +int +get_response_time(struct proc *p) +{ + return p->rutime - p->ctime; +} + +int set_prio(int priority) +{ + acquire(&ptable.lock); + + if (myproc()->killed || priority < 1 || priority > 3) + { + return -1; + } + + release(&ptable.lock); + + myproc()->priority = priority; + + return 0; +} + +int wait2(int *retime, int *rutime, int *stime) { + struct proc *p; + + int pid; + int havekids = 0; // Variável para verificar se existem filhos + + acquire(&ptable.lock); // Adquire o lock da tabela de processos + + for (;;) { // Loop infinito para aguardar até que um processo filho termine + havekids = 0; + // Percorre a lista de processos + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + // Verifica se o processo atual é pai do processo em análise + if (p->parent != myproc()) { + continue; // Se não for pai, continua para o próximo processo + } + havekids = 1; // Marca que há pelo menos um filho + // Se o processo filho terminou (estado ZOMBIE) + if (p->state == ZOMBIE) { + // Coleta estatísticas do processo filho + *stime = p->stime; + *retime = p->retime; + *rutime = p->rutime; + pid = p->pid; // Obtém o PID do processo filho + // Libera recursos associados ao processo filho + kfree(p->kstack); + p->kstack = 0; + freevm(p->pgdir); + p->state = UNUSED; // Marca o estado como UNUSED + p->pid = 0; // Reseta o PID + p->parent = 0; // Reseta o pai + p->name[0] = 0; // Reseta o nome + p->killed = 0; // Reseta a flag de kill + p->ctime = 0; // Reseta o tempo de criação + p->stime = 0; // Reseta o tempo em estado de espera + p->retime = 0; // Reseta o tempo de execução + p->rutime = 0; // Reseta o tempo em estado de execução + release(&ptable.lock); // Libera o lock da tabela de processos + return pid; // Retorna o PID do processo filho + } + } + + // Se não houver processos filhos ou se o processo atual foi sinalizado para terminar + if (!havekids || myproc()->killed) { + release(&ptable.lock); // Libera o lock da tabela de processos + return -1; // Retorna -1 indicando que não há processos filhos para esperar + } + + sleep(myproc(), &ptable.lock); // Aguarda até que um processo filho termine + } +} diff --git a/proc.h b/proc.h index 1647114179..b58b4ac6a3 100644 --- a/proc.h +++ b/proc.h @@ -49,6 +49,12 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) + int timeslice_tracker; // Time RUNNING uninterrupted + int ctime; // Time process was started + int stime; // Time SLEEPING + int retime; // Time RUNNABLE + int rutime; // Time RUNNING + int priority; // priority }; // Process memory is laid out contiguously, low addresses first: diff --git a/sanity.c b/sanity.c new file mode 100644 index 0000000000..7e3dc1c2b1 --- /dev/null +++ b/sanity.c @@ -0,0 +1,110 @@ +#include "types.h" +#include "user.h" +#define CPU_BOUND 0 +#define S_BOUND 1 +#define IO_BOUND 2 + +void CPU_Bound (void) { + for (int i = 0; i < 100; i++) { + for (int j = 0; j < 1000000; j++){} + } +} + +void S_Bound (void) { + for (int i = 0; i < 20; i++) { + for (int j = 0; j < 1000000; j++){} + yield(); + } +} + +void IO_Bound (void) { + for (int i = 0; i < 100; i++) { + sleep(1); + } +} + +void Create_Process (int type) { + switch (type) { + case CPU_BOUND: + CPU_Bound (); + break; + + case S_BOUND: + S_Bound (); + break; + + case IO_BOUND: + IO_Bound (); + break; + } +} + +int main (int argc, char *argv[]) { + + if (argc != 2) { + printf (1, "One parameter expected\n"); + exit(); + } + + int num_processes = atoi(argv[1]) * 3; //quantidade de processos + int processes_amount[3] = {0,0,0}; + int sum_ready_time[3] = {0,0,0}; + int sum_sleep_time[3] = {0,0,0}; + int sum_turnaround_time[3] = {0,0,0}; + + for (int i = 0; i < num_processes; i++) { + int process_id = fork(); + if (process_id == 0) { //processo filho + int type = getpid() % 3; + Create_Process (type); //chama funcao para executar a atividade + exit(); + } + else { //processo pai + //atualiza contador de processos para o tipo de atividade do processo filho + processes_amount[process_id % 3]++; + } + } + + // Chamar função wait2 e calcular rutime, retime e stime médios + for (int i = 0; i < num_processes; i++){ + int retime, rutime, stime; + int childPid = wait2 (&retime, &rutime, &stime); + char *type; + if ( childPid % 3 == 0){ + type = "CPU_BOUND"; + } else if (childPid % 3 == 1){ + type = "S_BOUND"; + } else{ + type = "IO_BOUND"; + } + + printf (1, "PID: %d\n Type: %s\n Ready time: %d\n Run time: %s\n Sleep time: %d\n\n", childPid, type, retime, rutime, stime); + sum_ready_time[childPid % 3] += retime; + sum_sleep_time[childPid % 3] += stime; + sum_turnaround_time[childPid % 3] += retime + rutime + stime; + } + + //calcula e imprime as medias + for (int j = 0; j < 3; j++) { + char *type; + if (j == 0) { + type = "CPU_BOUND"; + } + else if (j == 1) { + type = "S_BOUND"; + } + else { + type = "IO_BOUND"; + } + + printf (1, "%s Contador de processos: %d\n", type, processes_amount[j]); + printf (1, "%s Média ready time: %d\n", type, sum_ready_time[j] / processes_amount[j]); + printf (1, "%s Média sleeping time: %d\n", type, sum_sleep_time[j] / processes_amount[j]); + printf (1, "%s Média turnaround time: %d\n", type, sum_turnaround_time[j] / processes_amount[j]); + + + } + + exit(); + +} \ No newline at end of file diff --git a/spinlock.c b/spinlock.c index 402018643d..86d4db3ac0 100644 --- a/spinlock.c +++ b/spinlock.c @@ -27,7 +27,7 @@ acquire(struct spinlock *lk) pushcli(); // disable interrupts to avoid deadlock. if(holding(lk)) panic("acquire"); - + // The xchg is atomic. while(xchg(&lk->locked, 1) != 0) ; diff --git a/syscall.c b/syscall.c index ee85261602..8555bb795e 100644 --- a/syscall.c +++ b/syscall.c @@ -103,6 +103,10 @@ extern int sys_unlink(void); extern int sys_wait(void); extern int sys_write(void); extern int sys_uptime(void); +extern int sys_yield(void); +extern int sys_set_prio(void); +extern int sys_wait2(void); + static int (*syscalls[])(void) = { [SYS_fork] sys_fork, @@ -126,6 +130,9 @@ static int (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_yield] sys_yield, +[SYS_set_prio] sys_set_prio, +[SYS_wait2] sys_wait2 }; void diff --git a/syscall.h b/syscall.h index bc5f35651c..73549b9059 100644 --- a/syscall.h +++ b/syscall.h @@ -20,3 +20,7 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_yield 22 +#define SYS_set_prio 23 +#define SYS_wait2 24 + diff --git a/sysproc.c b/sysproc.c index 0686d295b6..125f65208a 100644 --- a/sysproc.c +++ b/sysproc.c @@ -89,3 +89,40 @@ sys_uptime(void) release(&tickslock); return xticks; } + +int +sys_yield(void) { + yield(); + return 0; +} + +int +sys_set_prio(void) +{ + int priority; + if(argint(0, &priority) < 0){ + return -1; + } + + return set_prio(priority); +} + +int +sys_wait2(void) +{ + int *retime; + int *rutime; + int *stime; + + if (argptr(0, (char**)&retime, sizeof(int)) < 0) + return -1; + + if (argptr(1, (char**)&rutime, sizeof(int)) < 0) + return -1; + + if (argptr(2, (char**)&stime, sizeof(int)) < 0) + return -1; + + return wait2(retime, rutime, stime); +} + diff --git a/trap.c b/trap.c index 41c66ebf9a..c1260481fd 100644 --- a/trap.c +++ b/trap.c @@ -51,6 +51,7 @@ trap(struct trapframe *tf) if(cpuid() == 0){ acquire(&tickslock); ticks++; + clock(); wakeup(&ticks); release(&tickslock); } @@ -103,8 +104,11 @@ trap(struct trapframe *tf) // Force process to give up CPU on clock tick. // If interrupts were on while locks held, would need to check nlock. if(myproc() && myproc()->state == RUNNING && - tf->trapno == T_IRQ0+IRQ_TIMER) + tf->trapno == T_IRQ0+IRQ_TIMER && + myproc()->timeslice_tracker == INTERV) { + myproc()->timeslice_tracker = 0; yield(); + } // Check if the process has been killed since we yielded if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER) diff --git a/user.h b/user.h index 4f99c52ba6..cdb3b3b5bb 100644 --- a/user.h +++ b/user.h @@ -23,6 +23,9 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +void yield(void); +int setprio(int); +int wait2(int*, int*, int*); // ulib.c int stat(const char*, struct stat*); diff --git a/usys.S b/usys.S index 8bfd8a1bc4..8fcb4a3623 100644 --- a/usys.S +++ b/usys.S @@ -29,3 +29,6 @@ SYSCALL(getpid) SYSCALL(sbrk) SYSCALL(sleep) SYSCALL(uptime) +SYSCALL(yield) +SYSCALL(wait2) +SYSCALL(set_prio) \ No newline at end of file