void interrupt(registers_t *reg) { // The processor responds to a system call interrupt by saving some of // the application's state on the kernel's stack, then jumping to // kernel assembly code (in mpos-int.S, for your information). // That code saves more registers on the kernel's stack, then calls // interrupt(). The first thing we must do, then, is copy the saved // registers into the 'current' process descriptor. current->p_registers = *reg; switch (reg->reg_intno) { case INT_SYS_GETPID: // The 'sys_getpid' system call returns the current // process's process ID. System calls return results to user // code by putting those results in a register. Like Linux, // we use %eax for system call return values. The code is // surprisingly simple: current->p_registers.reg_eax = current->p_pid; run(current); case INT_SYS_FORK: // The 'sys_fork' system call should create a new process. // You will have to complete the do_fork() function! current->p_registers.reg_eax = do_fork(current); run(current); case INT_SYS_YIELD: // The 'sys_yield' system call asks the kernel to schedule a // different process. (MiniprocOS is cooperatively // scheduled, so we need a special system call to do this.) // The schedule() function picks another process and runs it. schedule(); case INT_SYS_EXIT: // 'sys_exit' exits the current process, which is marked as // non-runnable. // The process stored its exit status in the %eax register // before calling the system call. The %eax REGISTER has // changed by now, but we can read the APPLICATION's setting // for this register out of 'current->p_registers'. current->p_exit_status = current->p_registers.reg_eax; if (current->p_waitqueue > 0) { proc_array[current->p_waitqueue].p_registers.reg_eax = current->p_exit_status; proc_array[current->p_waitqueue].p_state = P_RUNNABLE; } current->p_state = P_EMPTY; schedule(); case INT_SYS_NEWTHREAD: // system call to sys_newthread; starts with an empty stack, starts by executing the start_function (the function's address becomes the new thread's instruction pointer) current->p_registers.reg_eax = do_newthread(current, current->p_registers.reg_eax); run(current); case INT_SYS_WAIT: { // 'sys_wait' is called to retrieve a process's exit status. // It's an error to call sys_wait for: // * A process ID that's out of range (<= 0 or >= NPROCS). // * The current process. // * A process that doesn't exist (p_state == P_EMPTY). // (In the Unix operating system, only process P's parent // can call sys_wait(P). In MiniprocOS, we allow ANY // process to call sys_wait(P).) pid_t p = current->p_registers.reg_eax; if (p <= 0 || p >= NPROCS || p == current->p_pid || proc_array[p].p_state == P_EMPTY) current->p_registers.reg_eax = -1; else if (proc_array[p].p_state == P_ZOMBIE) current->p_registers.reg_eax = proc_array[p].p_exit_status; else { proc_array[p].p_waitqueue = current->p_pid; current->p_state = P_BLOCKED; } schedule(); } default: while (1) /* do nothing */; } }
void interrupt(registers_t *reg) { // The processor responds to a system call interrupt by saving some of // the application's state on the kernel's stack, then jumping to // kernel assembly code (in mpos-int.S, for your information). // That code saves more registers on the kernel's stack, then calls // interrupt(). The first thing we must do, then, is copy the saved // registers into the 'current' process descriptor. current->p_registers = *reg; switch (reg->reg_intno) { case INT_SYS_GETPID: // The 'sys_getpid' system call returns the current // process's process ID. System calls return results to user // code by putting those results in a register. Like Linux, // we use %eax for system call return values. The code is // surprisingly simple: current->p_registers.reg_eax = current->p_pid; run(current); case INT_SYS_FORK: // The 'sys_fork' system call should create a new process. // You will have to complete the do_fork() function! current->p_registers.reg_eax = do_fork(current); run(current); case INT_SYS_NEWTHREAD: // The 'sys_newthread' system call creates a new thread process, which // shares the same space as the calling process. The thread begins // executing the start_function. Returns the child thread's process id. current->p_registers.reg_eax = do_newthread(current, current->p_registers.reg_eax); run(current); case INT_SYS_YIELD: // The 'sys_yield' system call asks the kernel to schedule a // different process. (MiniprocOS is cooperatively // scheduled, so we need a special system call to do this.) // The schedule() function picks another process and runs it. schedule(); case INT_SYS_EXIT: // 'sys_exit' exits the current process, which is marked as // non-runnable. // The process stored its exit status in the %eax register // before calling the system call. The %eax REGISTER has // changed by now, but we can read the APPLICATION's setting // for this register out of 'current->p_registers'. current->p_state = P_ZOMBIE; current->p_exit_status = current->p_registers.reg_eax; if (current->p_waiting != NULL) // wake any sleeping processes { current->p_waiting->p_state = P_RUNNABLE; current->p_waiting->p_registers.reg_eax = current->p_exit_status; current->p_state = P_EMPTY; // free completed process } schedule(); case INT_SYS_KILL: { // 'sys_kill' forces a process to exit. It's an error to call sys_kill // for an out-of-range PID, the current process, a process that doesn't // exist or is dead. Returns 0 if successful, -1 if not. pid_t p = current->p_registers.reg_eax; process_t* target = &proc_array[p]; if (p <= 0 || p >= NPROCS || p == current->p_pid || proc_array[p].p_state == P_EMPTY) current->p_registers.reg_eax = -1; else if (target->p_state != P_ZOMBIE)// perform exit operations { target->p_state = P_ZOMBIE; target->p_exit_status = -1; if (target->p_waiting != NULL) // wake any sleeping processes { target->p_waiting->p_state = P_RUNNABLE; target->p_waiting->p_registers.reg_eax = target->p_exit_status; target->p_state = P_EMPTY; // free completed process } current->p_registers.reg_eax = 0; // successful, return 0 } else // process already exited current->p_registers.reg_eax = 0; run(current); } case INT_SYS_WAIT: { // 'sys_wait' is called to retrieve a process's exit status. // It's an error to call sys_wait for: // * A process ID that's out of range (<= 0 or >= NPROCS). // * The current process. // * A process that doesn't exist (p_state == P_EMPTY). // (In the Unix operating system, only process P's parent // can call sys_wait(P). In MiniprocOS, we allow ANY // process to call sys_wait(P).) pid_t p = current->p_registers.reg_eax; if (p <= 0 || p >= NPROCS || p == current->p_pid || proc_array[p].p_state == P_EMPTY) current->p_registers.reg_eax = -1; else if (proc_array[p].p_state == P_ZOMBIE) { current->p_registers.reg_eax = proc_array[p].p_exit_status; proc_array[p].p_state = P_EMPTY; // free zombie process } else { proc_array[p].p_waiting = current; // add calling process to wait queue current->p_state = P_BLOCKED; // put calling process to sleep } schedule(); } default: while (1) /* do nothing */; } }