To get an idea of how an operating system's system calls interact with user
programs and device interrupt handlers, you're going to implement, in
pseudo-code, four short system calls and two short interrupt handlers.
This small ``system'' uses a pool of buffers to perform I/O between
application programs, an input device, and an output device.
The buffers in the buffer pool are in one of three states at any point in
These states can be implemented as separate linked lists, which you can
initialize and manipulate using, at the pseudo-code level, the functions
you implemented in Project 0.
- INFULL -- input buffer full and available to be returned to
the user on request.
- OUTFULL -- output buffer full and waiting to be output to
the I/O device when ready.
- EMPTY -- empty buffer available for either input or output.
There are four system calls that are invoked by the user to accomplish I/O:
- bufptr Gbufin() -- returns a pointer to the beginning of
the next full input buffer from INFULL. This should block if INFULL is empty.
- Rbufin(bufptr) -- places the buffer pointed to by bufptr back into EMPTY.
- bufptr Gbufout -- returns a pointer to the beginning of an
empty buffer from EMPTY into which the user places data to be
output. This should block if EMPTY is empty.
- Rbufout(bufptr) -- places the full output buffer pointed to
by bufptr onto the end of OUTFULL.
In addition, there are two interrupt handlers, one for input and one for
- IHinput -- called when an input operation has been
- IHoutput -- called when an output operation has been
The diagram below illustrates how a buffer can move between these different
states, and the role of the system software described above.