CS 318
Mar. 25, 1998
The major conceptual breakthrough with threads is a usable shared address space. From this, the two most important advantages are:
Data sharing is a double-edged sword. I consider it also to be a major disadvantage of threads. I've seen what happens when user level threads walk over each others stacks; these interactions can introduce extremely subtle bugs which are mind-numbingly difficult to track down. Processes are insulated from each other and any interaction can be minimized and controlled.
``Fair'' thread management is also a problem. User level threads can suffer starvation problems. Kernel level threads begin to approach the complexity of processes.
Write pseudo-code for the disk process and the two system level library functions. Clearly indicate the content of mailbox messages.
Here's an example with three user processes:
We can make several observations. The disk process is a ``well-known'' process, so all fread()s and fwrite()s will know what mailbox to use. On the other hand, the disk process must know what user mailbox is to be used for the reply, so the user's mailbox number must be part of the request. The request mailbox can certainly have a mixed set of read requests and write requests within.
A disk operation request message will contain these fields:
Send() does not block, therefore another mechanism must be used to block fread() and fwrite. The best choice is to perform a Receive, awaiting the status reply from the disk process.
The disk process communicates directly with the disk. It will perform all StartRead()s and StartWrite()s and busy wait on a disk.busy flag. Its request mailbox number is kept in the global variable DiskProcMbox, which is available to all processes.
Here are the functions:
int fread(int sector, char *buff) { buffer reply; // Assume every user process has a variable, MyMbox, which // is the mailbox number for the process' replies Send(DiskProcMbox, "<MyMbox>,<Read>,<sector>,<buff>"); Receive(MyMbox, &reply); return reply == "OK"; }
int fwrite(int sector, char *data) { buffer reply; Send(DiskProcMbox, "<MyMbox>,<Write>,<sector>,<data>"); Receive(MyMbox, &reply); return reply == "OK"; }
Here's the pseudo-code for the disk process:
DiskProcess() { buffer request; while (1) { Receive(DiskProcMbox, &request); // get next request // process next request if (request.type == "Write") StartWrite(disk, request.sector, request.data); else if (request.type == "Read") StartRead(disk, request.sector, request.buff); else { InvalidRequest(); // terminate offending process continue; // skip busy wait and Send() } while (disk.busy) // wait for I/O to complete ; Send(request.mbox, "<disk.status>") // disk.status is either "OK" // or "ERROR" } }
Draw four Gantt charts illustrating the execution of these processes using
FCFS (non-
preemptive), preemptive SJF, non-preemptive priority (a smaller
priority number implies a higher priority), and RR (quantum = 1).
Also, for each scheduling algorithm, answer the following:
SRT results in the smallest average waiting time.
int flag[2] = { FALSE, FALSE } /* flag[i] indicates that Pi wants to */ /* enter its critical section */ int turn = 0; /* turn indicates which process has */ /* priority in entering its critical */ /* section */ flag[i] = TRUE; while (flag[1 - i]) if (turn == 1 - i) { flag[i] = FALSE; while (turn == 1 - i) ; flag[i] = TRUE; } CSi; turn = 1 - i; flag[i] = FALSE;Prove that the algorithm satisfies the three requirements for a solution to the CS problem.