CSCC69: Operating Systems Assignment 1 Some of the slides were borrowed from csc369 course, U of T, St George Monitors • Locks – Provide mutual exclusion – 2 operations: acquire() and release() • Semaphores – Generalize locks with an integer count variable and a thread queue – 2 operations: wait() and signal() – If the integer count is negative, threads wait in a queue until another thread signals the semaphore • Monitors – An abstraction that encapsulates shared data and operations on it in such a way that only a single process at a time may be executing “in” the monitor More on Monitors • Programmer defines the scope of the monitor – ie: which data is “monitored” • Local data can be accessed only by the monitor’s procedures (not by any external procedures • Other processes that attempt to enter the monitor are blocked. They must first acquire the lock before becoming active in the monitor. • Only a single process executes inside the monitor. Assignment 1 – Task 2 • The thread system provides interrupts, control functions, and semaphores. • You will be extending the thread system of OS161 to provide additional, higher-level functions found in most thread libraries and will use that functionality to implement the process management system calls. pidinfo • OS/161's threads are “processes”. • Each thread has a pidinfo block – pid of this thread, pid of parent, exit status,… • All of the blocks are stored in a table – Access to this table must be protected since it is a shared resource – Global lock? Individual locks? – pid.c in src/kern/thread serves as a monitor to do exactly this Assignment 1- Task 2 • The starter code includes a simple thread id management system (see pid.c). • The system allocates an identifier a (pid) whenever a new thread is created. • Some of the functions that allow programmers to access and use pids have been written. • You should think of pid.c as implementing a monitor that controls access to the shared pid data structures. • The externally-callable functions in pid.c are the monitor's entry points. • All access to the pid data structures (including querying or modifying any fields) must be done through a function provided by pid.c. Task 2 • Read the specification and manual carefully • Think before you implement to achieve the correct solution. • Add other variable/helper functions as you need • Look at pid.c/pid.h and thread.c/thread.h • src/kern/arch/mips/locore/trap.c contains code for newly admitted processes. Task 2 • int thread_join(pid_t pid, int *status) thread_join suspends the execution of the calling thread until the thread identified by pid terminates by calling thread_exit • Errors: – ESRCH: No thread could be found corresponding to that specified by pid. – EINVAL: The thread has been detached. – EINVAL: The caller of thread_join is not the parent of the thread specified by pid. – EINVAL: The pid argument refers to the calling thread. • The man pages in the OS/161 distribution contain a description of the error return values that you must return. • If there are conditions that can happen that are not listed in the man page, consider adding one. – Note that if you add an error code to kern/include/kern/errno.h, you need to add a corresponding error message to the file user/lib/libc/string/strerror.c. get/waitpid() • • • • System calls for accessing pid - user level interface A pid, is a unique number that identifies a process. getpid() returns the pid of the current process waitpid() causes current process to wait on another process to exit, and get their exit status • Use the kernel threading functionalities you implement • The implementation of getpid() should be straightforward, assuming you have already taken care of allocating thread identifiers when a new thread is created • Your waitpid() system call should map nicely to the thread_join function in the kernel thread system. Fork() • fork() is the mechanism for creating new processes. • It should make a copy of the invoking process and make sure that the parent and child processes each observe the correct return value (that is, 0 for the child and the newly created pid for the parent). • The core of the fork() system call will use the thread_fork() function to create a new kernel thread. System Calls • User level applications are isolated from the OS in a different protection domain • System calls are a way of requesting the services of the OS – Method calls defined in user-level libraries “trap” from user-space to the OS in kernel-space • Once control traps to the kernel, a kernel-level system call handler is invoked to service the system call and return the appropriate results to user-space • Once the system call is complete, control is given back to the user-level application Trap Frame • The trap frame is used to pass information about the exception, storing the current processor state for later execution • The trap handler determines which syscall is being requested by looking at the corresponding code in the trap frame struct (tf->tf_a0) • Once the syscall finishes executing, the return value or error code is put back into the trap frame (tf->tf_v0) • Read the code: taking a look at the whole call stack to get a better idea OS161 System Call Example: time Creating a New System Call in OS161 • Hint: take a look at what has been done for sys___time system call. You can follow the same pattern. • Define the new system call code in src/kern/include/kern/syscall.h • Define the prototypes for the new syscall – Kernel space: src/kern/include/syscall.h – User space: src/user/include • Write the source code for it in src/kern/syscall/proc_syscalls.c • If necessary, define any new error codes in src/kern/include/kern/errno.h • Complete the cases in the handler switch statement in src/kern/arch/mips/syscall/syscall.c • Test your system calls with programs in user/testbin • Rebuild both kernel and user level program _exit System Call • exit only take on integer argument. • All we need to do is find our struct process entry using curthread->t_pid. • Then indicate that “I’ve exited” and fill the exitcode. The only thing to note that the exitcode must be made using the MACROs in kern/include/kern/wait.h. _exit Name _exit - terminate process Library Standard C Library (libc, -lc) Synopsis #include <unistd.h> Void _exit(int exitcode); Description Cause the current process to exit. The exit code exitcode is reported back to other process(es) via the waitpid() call. The process id of the exiting process should not be reused until all processes interested in collecting the exit code with waitpid have done so. (What "interested" means is intentionally left vague; you should design this.) Return Values _exit does not return.