Most page-fault service routines merely virtualize the memory address space by copying pages from disk in response to page faults. In theory, though, the software could do many other things with invalid pages. One option that has not been exploited by any system I know of is the option of implementing access to Unix-like pipes (interprocess communication channels) using load and store instructions interpreted by the page fault service routine. To implement this option, invalid pages in the address map must be able to be marked as representing pipes, and the page fault service routine must be modified to perform pipe operations when it detects a load or store operation on such a page.
Problem, part A: Write example code for a process that reads and copies data from one pipe to another using this implementation, assuming that the necessary pipes have already been created outside your code and that the stream of data to be copied is of infinite length.
Problem, part B: If you were designing the pipe interface, how would you handle the problem of special pipe operations such as writing an end-of-file mark on the pipe, and how would your implementation signal end-of-file to users reading from a pipe.
Problem, part C: Give a brief outline of how the page fault service routine would implement this pipe interface, with an emphasis on those areas that both differ from conventional page fault service and from conventional pipe implementations.
Problem, part A: Formally speaking, what are the resources this system allows to be locked and what deadlock model applies?
Problem, part B: Assuming that only one file server is involved, what problems would you encounter in implementing a reasonable deadlock detection algorithm for the server.
read(b,s) reads from standard input into buffer b and signals the semaphore s when the operation is complete.
delay(t,s) causes semaphore s to be signaled after a delay of t seconds.
Problem, part A: What behavior would you expect from the following code:
S: semaphore with S.count=0 loop read(b,S) delay(1.0,S) wait(S) process(b) end loopProblem, part B: Given that the desired operation is a timed read that either returns the input data or gives up on reading after one second, will the following version of this operate correctly? Please assume that infinite memory is available.
loop S = pointer to new semaphore with S.count=0 read(b,S^) delay(1.0,S^) wait(S^) process(b) end loop
main() { printf("%d\n", foo() + (foo()<<1) + (foo()<<2) + (foo()<<3) ); } foo() { return(fork() ? 1 : 0); }
Problem: When you run this ten times, you will most likely get a different answer each time. Given that the computer system itself is basically deterministic, for example, it has a deterministic scheduling algorithm running on a deterministic CPU, what is the source of the nondeterministic behavior in this program?
Furthermore, assume that makepage(va) creates a new page in the user's address space, and that killpage(va) irreversably deallocates a page. In addition, assume that the system uses demand paged virtual memory, so that pages can be moved to and from disk by the system, at will.
The problem, part A: What storage management problems must the user and system face in using this capability system? Can these problems be solved? Would you rely on this system for critical applications?
The problem, part B: Propose a representation of the capabilities given to the user by getcap.
For example, consider the idea of a virtual file server. This would use some real underlying file server to implement its operations, but it might change the semantics, for example by replicating files in order to provide fault tolerance.
The problem, part A: Explain how you would implement such a file server under a system such as DEMOS or Amoeba.
The problem, part B: Explain how you would design the system so that a user process would be unable to tell whether it was running under a real or virtual file server.