diff --git a/Makefile b/Makefile index 09d790cf63..d6eedd1929 100644 --- a/Makefile +++ b/Makefile @@ -181,6 +181,10 @@ UPROGS=\ _usertests\ _wc\ _zombie\ + _SJFtest\ + _fcfs_test\ + _wrr_test\ + _test_systemcalls\ fs.img: mkfs README $(UPROGS) ./mkfs fs.img README $(UPROGS) diff --git a/SJFtest.c b/SJFtest.c new file mode 100644 index 0000000000..775d3cdcaf --- /dev/null +++ b/SJFtest.c @@ -0,0 +1,44 @@ +#include "types.h" +#include "user.h" + +int main(void) +{ + int pid1, pid2, pid3; + + // Process 1 + pid1 = fork(); + if (pid1 == 0) { + // Child process 1 + setpriority(2); + setburst(10); // Set burst time for process 1 + setconfidence(80); // Set confidence for process 1 + while (1) ; // Infinite loop to simulate a long-running process + } + + // Process 2 + pid2 = fork(); + if (pid2 == 0) { + // Child process 2 + setpriority(2); + setburst(8); // Set burst time for process 2 + setconfidence(60); // Set confidence for process 2 + while (1) ; // Infinite loop to simulate a long-running process + } + + // Process 3 + pid3 = fork(); + if (pid3 == 0) { + // Child process 3 + setpriority(2); + setburst(12); // Set burst time for process 3 + setconfidence(90); // Set confidence for process 3 + while (1) ; // Infinite loop to simulate a long-running process + } + + // Parent process waits for children (optional, for cleanup purposes) + wait(); + wait(); + wait(); + + exit(); +} diff --git a/fcfs_test.c b/fcfs_test.c new file mode 100644 index 0000000000..946f52d4cd --- /dev/null +++ b/fcfs_test.c @@ -0,0 +1,31 @@ +#include "types.h" +#include "user.h" + +int main(int argc, char *argv[]) { + int num_children = 5; + int i; + for(i = 0; i < num_children; i++) { + int pid = fork(); + setpriority(3); + if(pid < 0){ + printf(1, "Fork failed\n"); + exit(); + } + if(pid == 0){ + // Child process + printf(1, "Child %d started (PID: %d)\n", i, getpid()); + // Simulate some work with sleep + sleep(100); // Adjust sleep duration as needed + printf(1, "Child %d finished (PID: %d)\n", i, getpid()); + exit(); + } + // Parent process continues to fork next child + } + + // Parent waits for all children to finish + for(i = 0; i < num_children; i++) { + wait(); + } + printf(1, "All children have finished.\n"); + exit(); +} diff --git a/proc.c b/proc.c index 806b1b184b..308235dbf9 100644 --- a/proc.c +++ b/proc.c @@ -7,6 +7,9 @@ #include "proc.h" #include "spinlock.h" + +//static uint global_creation_order = 0; + struct { struct spinlock lock; struct proc proc[NPROC]; @@ -65,6 +68,15 @@ myproc(void) { return p; } + +static unsigned int seed = 12345; + +int rand() +{ + seed = (1103515245 * seed + 12345) % 2147483648; + return seed; +} + //PAGEBREAK: 32 // Look in the process table for an UNUSED proc. // If found, change state to EMBRYO and initialize @@ -88,6 +100,11 @@ allocproc(void) found: p->state = EMBRYO; p->pid = nextpid++; + //p->creation_order = global_creation_order++; + p->creation_order = ticks; + p->priority = 3; + p->burst_time = rand()%20; + p->confidence = rand()%100; release(&ptable.lock); @@ -311,6 +328,20 @@ wait(void) } } + + +int +no_runnable_proc_except(struct proc *exclude_proc) +{ + struct proc *p; + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->state == RUNNABLE && p != exclude_proc) + return 0; // Found another runnable process + } + return 1; // No other runnable process +} + + //PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. @@ -319,42 +350,165 @@ wait(void) // - 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); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state != RUNNABLE) - continue; +void scheduler(void) { + struct proc *p; + struct cpu *c = mycpu(); + c->proc = 0; + + // Initialize WRR counters for each priority + int weights[4] = {0, 3, 2, 1}; // WRR weights for priorities 1, 2, 3 + c->wrr_counter[1] = weights[1]; + c->wrr_counter[2] = weights[2]; + c->wrr_counter[3] = weights[3]; + + for (;;) { + sti(); // Enable interrupts + + acquire(&ptable.lock); + + struct proc *selected_proc = 0; + int selected_priority = 0; + + // WRR logic: Select the next priority queue + for (int priority = 1; priority <= 3; priority++) { + if (c->wrr_counter[priority] > 0) { + selected_priority = priority; + c->wrr_counter[priority]--; + break; + } + } + + // Reset WRR counters after a complete round + if (selected_priority == 0) { + c->wrr_counter[1] = weights[1]; + c->wrr_counter[2] = weights[2]; + c->wrr_counter[3] = weights[3]; + } + + if (selected_priority == 1) { + // Round-Robin Scheduler for Priority 1 + struct proc *rr_proc = 0; + int oldest_run = 1e9; + + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->state == RUNNABLE && p->priority == 1 && p->last_run_time < oldest_run) { + rr_proc = p; + oldest_run = p->last_run_time; + } + if ( (p->state == RUNNABLE) && (p->pid > 2) ) + p->age++; + // else + // p->age=0; + } + + if (rr_proc) { + selected_proc = rr_proc; + cprintf("Running process PID=%d from Priority 1 (RR)\n", selected_proc->pid); + cprintf("Running process age=%d from Priority 1 (RR)\n", selected_proc->age); + + c->proc = selected_proc; + switchuvm(selected_proc); + selected_proc->state = RUNNING; + selected_proc->last_run_time = ticks; // Update the last run time + selected_proc->age=0; + + // Save process state during the switch + swtch(&(c->scheduler), selected_proc->context); + switchkvm(); + + // Process resumes here after switching back + if (selected_proc->state == RUNNING) { + selected_proc->state = RUNNABLE; // Mark it runnable again + } + + c->proc = 0; + } + } else if (selected_priority == 2) { + // SJF with Confidence Scheduler for Priority 2 + struct proc *sjf_proc = 0; + int min_burst = 1e9; + + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->state == RUNNABLE && p->priority == 2 && p->burst_time < min_burst) { + sjf_proc = p; + min_burst = p->burst_time; + } + } + if (sjf_proc) { + int rand_num = rand() % 100; + if (rand_num <= sjf_proc->confidence || sjf_proc->confidence == 0) { + cprintf("Scheduler: Running Process PID=%d, Burst=%d, Confidence=%d (SJF)\n", + sjf_proc->pid, sjf_proc->burst_time, sjf_proc->confidence); + + c->proc = sjf_proc; + switchuvm(sjf_proc); + sjf_proc->state = RUNNING; + sjf_proc->age=0; + swtch(&(c->scheduler), sjf_proc->context); + switchkvm(); + c->proc = 0; + } + } + } else if (selected_priority == 3) { + // FCFS Scheduler for Priority 3 + struct proc *fcfs_proc = 0; + int earliest_order = 1e9; + + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->state == RUNNABLE && p->priority == 3 && p->creation_order < earliest_order) { + fcfs_proc = p; + earliest_order = p->creation_order; + } + } + + if (fcfs_proc) { + selected_proc = fcfs_proc; + cprintf("Running process PID=%d from Priority 3 (FCFS)\n", selected_proc->pid); + + c->proc = selected_proc; + switchuvm(selected_proc); + selected_proc->state = RUNNING; + selected_proc->age=0; + swtch(&(c->scheduler), selected_proc->context); + switchkvm(); + + // Process resumes here after switching back + if (selected_proc->state == RUNNING) { + selected_proc->state = RUNNABLE; // Mark it runnable again + } + + c->proc = 0; + } + } + + // Fallback: Select any runnable process if no priority-based process was executed + if (!c->proc) { + struct proc *fallback_proc = 0; + + for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->state == RUNNABLE && p->priority == 0) { + fallback_proc = p; + break; + } + } + + if (fallback_proc) { + cprintf("Running process PID=%d from Priority 0 (Fallback)\n", fallback_proc->pid); + c->proc = fallback_proc; + switchuvm(fallback_proc); + fallback_proc->state = RUNNING; + swtch(&(c->scheduler), fallback_proc->context); + switchkvm(); + c->proc = 0; + } + } - // Switch to chosen process. It is the process's job - // to release ptable.lock and then reacquire it - // before jumping back to us. - c->proc = p; - switchuvm(p); - p->state = RUNNING; - - 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 @@ -532,3 +686,19 @@ procdump(void) cprintf("\n"); } } + +void age_stuff(void) { + acquire(&ptable.lock); + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if ( (p->state == RUNNABLE) && (p->pid > 2) ) + p->age++; + else + p->age=0; + if ( (p->priority != 1) && (p->pid > 2) && (p->age >= 80) ) { + p->age=0; + p->priority--; + p->creation_order = ticks; + } + } + release(&ptable.lock); +} \ No newline at end of file diff --git a/proc.h b/proc.h index 1647114179..c713472383 100644 --- a/proc.h +++ b/proc.h @@ -8,6 +8,7 @@ struct cpu { int ncli; // Depth of pushcli nesting. int intena; // Were interrupts enabled before pushcli? struct proc *proc; // The process running on this cpu or null + int wrr_counter[4]; // Time slices left for priorities 1, 2, 3 (index 0 unused) }; extern struct cpu cpus[NCPU]; @@ -49,6 +50,13 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) + int burst_time; // Burst time of the process + int confidence; // Confidence level (0 to 99) + uint creation_order; // New field to track creation order + int priority; // Priority: 1 (RR), 2 (SJF with confidence), 3 (FCFS) + int last_run_time; // Tracks the last time the process ran (used for RR) + int age; + int quantum_used; }; // Process memory is laid out contiguously, low addresses first: @@ -56,3 +64,5 @@ struct proc { // original data and bss // fixed-size stack // expandable heap + +void age_stuff(void); \ No newline at end of file diff --git a/sjf.zip b/sjf.zip new file mode 100644 index 0000000000..6768f29c1b Binary files /dev/null and b/sjf.zip differ diff --git a/syscall.c b/syscall.c index ee85261602..8bb12732ed 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_setburst(void); +extern int sys_setconfidence(void); +extern int sys_setpriority(void); +extern int sys_printinfo(void); static int (*syscalls[])(void) = { [SYS_fork] sys_fork, @@ -126,6 +130,10 @@ static int (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_setburst] sys_setburst, +[SYS_setconfidence] sys_setconfidence, +[SYS_setpriority] sys_setpriority, +[SYS_printinfo] sys_printinfo, }; void diff --git a/syscall.h b/syscall.h index bc5f35651c..f20a208441 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_setburst 22 +#define SYS_setconfidence 23 +#define SYS_setpriority 24 +#define SYS_printinfo 25 \ No newline at end of file diff --git a/sysproc.c b/sysproc.c index 0686d295b6..41b35249a3 100644 --- a/sysproc.c +++ b/sysproc.c @@ -6,7 +6,11 @@ #include "memlayout.h" #include "mmu.h" #include "proc.h" - +#include "spinlock.h" +extern struct { + struct spinlock lock; + struct proc proc[NPROC]; +} ptable; int sys_fork(void) { @@ -89,3 +93,56 @@ sys_uptime(void) release(&tickslock); return xticks; } + +int +sys_setburst(void) +{ + int burst; + + if(argint(0, &burst) < 0 || burst <= 0) + return -1; // Invalid burst time + myproc()->burst_time = burst; + return 0; +} + +int +sys_setconfidence(void) +{ + int conf; + + if(argint(0, &conf) < 0 || conf < 0 || conf > 99) + return -1; // Confidence must be in the range 0-99 + myproc()->confidence = conf; + return 0; +} + +int sys_setpriority(void) { + int prio; + if (argint(0, &prio) < 0 || prio < 1 || prio > 3) + return -1; + myproc()->priority = prio; + return 0; +} + +int sys_printinfo(void) +{ + acquire(&ptable.lock); + struct proc *p; + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->pid > 0) { + static char* state_names[] = { [UNUSED] "UNUSED" , [EMBRYO] "EMBRYO" , [SLEEPING] "SLEEPING" , [RUNNABLE] "RUNNABLE" , [RUNNING] "RUNNING" , [ZOMBIE] "ZOMBIE"}; + cprintf("----------------------\n"); + cprintf("name: %s - ",p->name); + cprintf("pid: %d - ",p->pid); + cprintf("state: %s - ",state_names[p->state]); + cprintf("burst time: %d - ",p->burst_time); + cprintf("confidence: %d - ",p->confidence); + cprintf("create time: %d - ",p->creation_order); + cprintf("priority: %d - ",p->priority); + cprintf("last run time: %d\n",p->last_run_time); + cprintf("-----------------------\n"); + } + } + release(&ptable.lock); + return 0; +} diff --git a/test_systemcalls.c b/test_systemcalls.c new file mode 100644 index 0000000000..435f81a86d --- /dev/null +++ b/test_systemcalls.c @@ -0,0 +1,48 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + + +void stall(int id) +{ + volatile long long x = 0; + for (int i = 0; i < 100000000; i++) + { + if (i % 10000000 == 0){ + printinfo(); + } + x += i; + } +} + +int main(void) +{ + int pid; + for (int i = 0; i < 10; i++) + { + pid = fork(); + if (pid == 0) + { + if (i < 5) + { + printf(1, "Set queue called for process: %d\n" , getpid()); + setpriority(2); + } + if (i < 5) + { + printf(1, "Set burst confidence called for process: %d\n" , getpid()); + setburst(1); + setconfidence(70); + } + // Child process + stall(getpid()); + exit(); + } + + + } + while (wait() > 0); + + printf(1, "All processes done\n\n"); + exit(); +} diff --git a/trap.c b/trap.c index 41c66ebf9a..fd96ee7b51 100644 --- a/trap.c +++ b/trap.c @@ -33,44 +33,58 @@ idtinit(void) } //PAGEBREAK: 41 -void -trap(struct trapframe *tf) -{ - if(tf->trapno == T_SYSCALL){ - if(myproc()->killed) +// Global variables for priority time quanta +int priority_time[4]; +int current_priority = 1; // Start with priority 1 + +void init_priority_time() { + priority_time[1] = 3 * ticks; + priority_time[2] = 2 * ticks; + priority_time[3] = 1 * ticks; +} + +void trap(struct trapframe *tf) { + if (tf->trapno == T_SYSCALL) { + if (myproc()->killed) exit(); myproc()->tf = tf; syscall(); - if(myproc()->killed) + if (myproc()->killed) exit(); return; } - switch(tf->trapno){ + switch (tf->trapno) { case T_IRQ0 + IRQ_TIMER: - if(cpuid() == 0){ + if (cpuid() == 0) { acquire(&tickslock); ticks++; wakeup(&ticks); release(&tickslock); } + lapiceoi(); break; + case T_IRQ0 + IRQ_IDE: ideintr(); lapiceoi(); break; - case T_IRQ0 + IRQ_IDE+1: + + case T_IRQ0 + IRQ_IDE + 1: // Bochs generates spurious IDE1 interrupts. break; + case T_IRQ0 + IRQ_KBD: kbdintr(); lapiceoi(); break; + case T_IRQ0 + IRQ_COM1: uartintr(); lapiceoi(); break; + case T_IRQ0 + 7: case T_IRQ0 + IRQ_SPURIOUS: cprintf("cpu%d: spurious interrupt at %x:%x\n", @@ -78,35 +92,47 @@ trap(struct trapframe *tf) lapiceoi(); break; - //PAGEBREAK: 13 default: - if(myproc() == 0 || (tf->cs&3) == 0){ - // In kernel, it must be our mistake. + if (myproc() == 0 || (tf->cs & 3) == 0) { cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n", tf->trapno, cpuid(), tf->eip, rcr2()); panic("trap"); } - // In user space, assume process misbehaved. cprintf("pid %d %s: trap %d err %d on cpu %d " "eip 0x%x addr 0x%x--kill proc\n", myproc()->pid, myproc()->name, tf->trapno, tf->err, cpuid(), tf->eip, rcr2()); myproc()->killed = 1; } + // Aging and queue adjustment + age_stuff(); + // Handle priority-based time-slicing + if (myproc() && myproc()->state == RUNNING && tf->trapno == T_IRQ0 + IRQ_TIMER) { + priority_time[current_priority] -= 1; - // Force process exit if it has been killed and is in user space. - // (If it is still executing in the kernel, let it keep running - // until it gets to the regular system call return.) - if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER) - exit(); + if (priority_time[current_priority] <= 0) { + // Reset time for the current priority + if (current_priority == 1) { + priority_time[1] = 30; + } else if (current_priority == 2) { + priority_time[2] = 20; + } else if (current_priority == 3) { + priority_time[3] = 10; + } - // 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) - yield(); + // Move to the next priority + current_priority++; + if (current_priority > 3) { + current_priority = 1; // Wrap around + } - // Check if the process has been killed since we yielded - if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER) + yield(); // Yield the CPU + } + } + + if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER) exit(); -} + + if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER) + exit(); +} \ No newline at end of file diff --git a/user.h b/user.h index 4f99c52ba6..2fd59b5015 100644 --- a/user.h +++ b/user.h @@ -23,6 +23,10 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +int setburst(int burst); +int setconfidence(int confidence); +int setpriority(int priority); +int printinfo(void); // ulib.c int stat(const char*, struct stat*); diff --git a/usys.S b/usys.S index 8bfd8a1bc4..4327cc4592 100644 --- a/usys.S +++ b/usys.S @@ -29,3 +29,7 @@ SYSCALL(getpid) SYSCALL(sbrk) SYSCALL(sleep) SYSCALL(uptime) +SYSCALL(setburst) +SYSCALL(setconfidence) +SYSCALL(setpriority) +SYSCALL(printinfo) \ No newline at end of file diff --git a/wrr_test.c b/wrr_test.c new file mode 100644 index 0000000000..ccdfd0e11b --- /dev/null +++ b/wrr_test.c @@ -0,0 +1,55 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +#define NUM_OF_PROCESSES 60 +#define job_ITERATIONS 10000000 + +void job(int id) +{ + volatile long long x = 0; + for (long long i = 0; i < job_ITERATIONS; i++) + x += i; +} + +static unsigned int seed = 12345; + +int rand() +{ + seed = (1103515245 * seed + 12345) % 2147483648; + return seed; +} + +int main(void) +{ + int pid; + for (int i = 0; i < NUM_OF_PROCESSES; i++) + { + pid = fork(); + // if (i <= 20){ + // setpriority(1); + // }else if ( 20 < i && i <=40){ + // setpriority(2); + // setburst(rand()%20); + // setconfidence(rand()%100); + // }else { + // setpriority(3); + // } + if (pid < 0) + { + printf(1, "Fork failed\n"); + exit(); + } + else if (pid == 0) + { + // Child process + job(i); + exit(); + } + } + + while (wait() > 0); + + printf(1, "All processes done\n\n"); + exit(); +}