Submit: Turn in your
entire xinu-hw9
directory source files
using the turnin command
on morbius.mscsnet.mu.edu or one of the other
Systems Lab machines. Please run "make clean" in
the
compile/ subdirectory just before submission to reduce
unnecessary space consumption.
Work should be completed in pairs. Be certain to include both names in the comment block at the top of all source code files, with a TA-BOT:MAILTO comment line including any addresses that should automatically receive results. It would be courteous to confirm with your partner when submitting the assignment.
Make a fresh copy of your work thus far.
cp -R xinu-hw8 xinu-hw9
Then, untar the new project files on top of it:
tar xvzf ~brylow/os/Projects/xinu-hw9.tgz
Be certain to make clean before compiling for the first time.
The distinction between the concepts of "threads" and "processes" can be an important one. Our textbook provides the definition that processes execute in their own memory space, whereas threads share the same memory space within a process. But beyond that, most of the aspects of processes we have already studied seem to repeat for threads -- you need a thread control block, each thread gets its own stack of activation records, we need to worry about a scheduling policy for deciding which thread we will context switch to next, etc.
So which do we have in our Embedded Xinu operating system that we've been building? Processes or threads? What we've been calling processes do not have their own independent address space -- there is only one page table that is being shared across all of the processes. In that sense, we could view our entire operating system as just one lone process scheduling multiple "threads" across the cores. (And we could certainly go through and replace the word "process" everywhere in the source code with the word "thread" to enforce that definition.) On the other hand, we've implemented a simple system call interface so that processes need to go through a trap handler to access protected kernel functions, so aspects of our system already behave as though each process exists in a lower hardware privilege level, and only the kernel has full run of the system in supervisor mode.
If we had time in a more advanced version of this course, we could invest our efforts in building an independent page table for each process that would provided it a more restricted, virtual memory layout, and then it would be much clearer that what we've been calling a process is indeed a process.
In truth, in the context of a simplified, embedded, educational operating system like ours, the distinction between processes and threads is not very relevant. We could label it either way, and build out the system in more concrete ways to reinforce that interpretation. In this assignment, we muddy the waters slightly further by implementing a crucial subset of the standard PThreads API using what the lightweight processes we've been working with all along.
The new tarball provides an include/pthreads.h header that defines the crucial new structs, #defines and function prototypes to mimic the standard POSIX PThreads interface. Chapter 27 in our textbook explains how this API works, and gives excellent examples.
The mapping of these PThread functions to existing Embedded Xinu functions is the primary content of this assignment.
Each of the five PThread API functions above should be implemented as system calls by extending the trap handlers we built in Project 5: Trap Handlers.
Begin be adding trap handlers for getmem() and freemem() from our previous assignment. (Good question for the final exam: "Why should getmem() and freemem() be implemented as system calls, but not malloc() and free()?)
First, add new trap numbers to include/syscall.h:
#define SYSCALL_GETMEM 17 /**< Allocate heap memory */
#define SYSCALL_FREEMEM 18 /**< Deallocate heap memory */
Second, add new entries to the global syscall_table in syscall_dispatch.c. Note that the row in the table must correspond to the correct syscall numbers defined above. The first column in the table is the number of arguments the syscall expects.
Finally, add the new front-end and backend calls to connect things up. For getmem() and freemem(), we can define the user-facing as getmem() and freemem(), and rename the behind-the-scenes implementations as sc_getmem() and sc_freemem().
Repeat this process for our new PThreads interface:
#define SYSCALL_PTCREATE 19 /**< PThread create */
#define SYSCALL_PTJOIN 20 /**< PThread join */
#define SYSCALL_PTLOCK 21 /**< PThread mutex lock */
#define SYSCALL_PTUNLOCK 22 /**< PThread mutex unlock */
#define SYSCALL_PTTRYLOCK 23 /**< PThread mutex trylock */
When all is said and done, you should be able to compile and execute our lab demo PThread code correctly on your Embedded Xinu kernel.
Note: If you lack confidence in prior stages of your kernel, it is OK to comment out the calls to unparkcore() in initialize.c and work with only Core 0 for this project. With preemption, this PThreads API should still work correctly on just a single core.
[Revised 2020 Nov 5 11:31 DWB]