Operating Systems
CIS*3110 (W12)
Assignment #1
Due: February 6, 2011 at 23:55h
(please see deliverables section below)
Key concepts: System calls; signal handling; resource management.
Read the following sections carefully to ensure that you know what is to be
done and what is to be handed in. If the details aren't addressed
successfully you will lose marks. Please refer to the
coding style guidelines to provide
direction regarding the format of the code you write. You are expected to
work independently on this assignment (please see
academic misconduct in computing
for clarification if necessary).
Lockdown!
As an exercise to learn about signal handling, as well as expore some of the
issues that arise in resource managment, we'll contrive a system with some
unusual restrictions.
The idea in brief: in a misguided attempt at security, the operating system
has been configured such that standard I/O channels, including the file system,
are blocked to all processes. You have found a clever exploit to bypass this
security, but it will only work for a single running process. You'll attempt
to make use of this single process as a proxy for other processes to
communicate with one another as a chat system.
The Problem
Imagine that you wish to make it possible for processes to communicate with
one another in a UNIX environment; however, due to draconian security settings,
all normal means of communication (i.e. files, pipes, sockets, streams---we'll
learn about these later) have been blocked and made inaccessible. For
example, you can imagine that the system is heavily monitored and any attempt
to share a file, open a socket, etc. will be intercepted and your illicit act
will be discovered. You have found a way to circumvent this security
involving shared memory, but only a single process will be able to
perform these functions. You have decided to build that program to be a proxy
by which other processes can indirectly communicate using signals as
a means of inter-process coordination.
To test your plan, you are going to write two (2) programs in C. One of them,
called proxy, starts up and goes to sleep waiting for other processes
to interact with it (this is the only process that can access the file system,
or that can initialize a shared memory block). The other program you will
write is called proxychat which will implement instant messenger-style
behaviour allowing two chat clients to send strings to one another using the
proxy process as the intermediary; note that many pairs of chat clients should
be able to communicate at the same time without issue.
Note that the proxy process is the only process that is permitted to
perform operations directly on the file system, and is the only process that
can freely send/receive signals to/from other processes. The processes may
only use the defined shared memory blocks for data transfer between
processes, and can only use signals to communicate otherwise; however,
they are allowed to execute any other system calls that do not deal
with communication via other (forbidden) means.
You will need to carefully design a distributed "state" that is maintained at
all times while the proxy is running as it is possible for it to be interacting
with a number of different processes at any time and many events are
asynchronous. It falls to a protocol to ensure everything works properly over
time, regardless of the order in which events occur. This closely mimics the
issues associated with resource contention in the operating system (you can
think of the proxy resources similar to a single hard drive---there must be
a means by which orderly access is permitted to the single shared resource).
You can make any reasonable assumptions regarding system behaviour otherwise
(e.g. guaranteed delievery of signals, etc.) and can safely make use of these
assumptions in implementing your protocol.
Mode of operation
You have the option to design the global behaviour of this system in one of
two ways (that is to say, the choice is yours - implement it in one of these
two forms; you are not required to support both modes of operation):
- point-to-point: the proxychat clients talk to only
one other chat client. This would mimic an instant messager type
experience.
- broadcast: the proxychat clients are all connected
such that anything typed in any chat client is relayed to and appears
in all other chat clients. This would be most like an IRC, or other
"room-based" conversation system.
Neither of these is significantly easier or harder than the other; however,
the details you need to pay attention to differ slightly. For example, in
the broadcast case, it would be possible to let all clients read data from
the shared memory area at the same time, if you have a reasonable method for
tracking when they are all done.
Command-line operation
In the interest of consistency, you should adopt the following standards for
the command-line operation of your programs.
- proxy
The proxy shouldn't need any additional information in order to start up.
You should have the program output its PID when it starts up (to
facilitate providing it to the proxychat clients).
- proxychat <pid>
The proxychat needs at least the pid of the proxy in order to do anything
at all, so provide it as a command-line argument to the proxychat program.
Depending on how you are implementing your system (point-to-point or
broadcast), you may need to provide more information. If it is relevant,
the proxychat should also output its PID when it first starts up so the
user has it. If you are doing point-to-point communication and need to
specify the pid of the remote proxychat client, it should be prompted
for and input as the first thing expected once the proxychat starts (after
which the only input would be expected to be text to be communicated).
There may be other, more sophisticated, options you with to persue. Check
with the instructor to ensure it's not something that will complicate
marking too much.
Implementation Guidelines
Notes:
- There are (2) shared memory areas, one 4 bytes in size (to hold an
integer value), and the other 1024 bytes in size (a character buffer
for messages). These are to be initialized by the proxy process to
which all other processes attach when they are executed. This is what
will be used for data sent between chat processes. For simplicity, you
can assume transfers in blocks of 1024 bytes, and that it is only text
being transferred. Note that long messages may have to be broken up by
the clients (the proxy should not be involved in this distinction,
whatever you do needs to be handled by the chat processes themselves).
This behaviour is also distributed and must be managed purely from the
state of the system.
- The integer value in the shared memory area is specified as a hook for
anything you might find useful in your design. For example, it might
be used by the clients to indicate the pid of the recipient.
Alternatively you might use it to indicate the size of the data written
in the larger shared area, etc. If you have no need of this integer,
you need not implement it.
- Note since there is only a single set of shared resources
in the proxy, the protocol used to mitigate access by the external
processes must protect the integrity of this single resource, even though
it is potentially being used by multiple processes for different puprposes
at the same time. This is essentially a problem of resource management:
the proxy will be responsible for the record-keeping necessary to ensure
only one operation is ever being performed concurrently.
- The mechanism by which you will implement your protocol will be signals.
The general idea:
- proxychat
-
- signal handler(s) initialized to handle incoming signals from
proxy (either the proxy is giving us permission to accesss the
shared area based on our previously signaling the proxy for
attention, or it is telling us there is data in the shared area,
a message from the connected chat client for us)
- otherwise: read input from user, when user presses enter,
the line typed needs to be sent to the receiving process so
need to signal the proxy for attention; you can assume the
process waits until the line is sent successfully before
accepting more input.
- proxy
-
- signal handler(s) initialized to handle incoming signals from
chat clients (signal can come from any client, at any time,
requesting attention---as signals will come while the proxy may
be otherwise occupied, these requests for attention need to be
queued and processed in order).
- otherwise: loop on attention queue servicing requests in order.
servicing a request for attention involves:
- signal the requesting process that it has access to the
shared resoures, and sleep
- when woken by signal from requesting process, signal
destination process that it should consume the contents of
the shared area, and sleep
- when woken by signal from destination process that it is
done, continue looping on event queue
- It is critical that you fully understand the various states that
your chat processes and the proxy can be in, and what events can possibly
occur in a given state. In particular, a chat process may have requested
the attention of the proxy; however, it is still possible that a send from
the other chat process was pending and you'll receive a signal to consume
it while waiting for the proxy's attention to send your own. Given
appropriate assumptions, this should be fairly straightforward. If you
find it hugely complicated, you're probably thinking about it wrong.
- Note that you need to pay some attention to what happens when a signal
is received while a handler might be running. The default behaviour of
the handler may be to reset the handler to the default once triggered,
which might result in your program shutting down if it gets a signal
while handling a previous instance of the same signal. Consider using
SIG_IGN explicitly at the beginning of your handler so you know
it will ignore such signals. This means that you now have a situation
where a signal could be received, but ignored. If this will pollute the
"state" of the system, you should develop a method for using
acknowledgement signals and timeouts to control for this (the alarm(2)
system call can be used to arrange for a SIGALRM to be sent to itself
after a set time and can be used as a time-out for resend --- careful
that this works as you think it will and doesn't introduce new issues).
- You shouldn't need to use signals other than SIGUSR1 and
SIGUSR2. Feel free to use additional signals if you like, but
be very careful which pre-defined signals you co-opt for your own
purposes. For example, SIGIO is being sent by the OS to your
processes all the time and hooking your own handler to it will have it
being triggered for reasons other than signals you've sent from your
processes.
- While no one expects your system to be completely bullet-proof, you should
try to handle reasonable error states that might arise and report them
in a meaningful way to the user (as opposed to the whole system just
hanging). For example, if you are doing a broadcast-style system, how
do you handle the situation where a client you think you're connected
to dies during the read, and thus will never respond to the proxy that
it is done. You should be highlighting these design issues in your
documentation.
- You are permitted to use certain POSIX signal handling routines for this,
in particular signal(2) and sigaction(2) for setting up
signal handlers (you'll need to use sigaction(2) functionality
to be able to determine the pid of the signalling process when needed),
and the kill(1) system call for sending signals. You may
not use more sophisticated signaling mechanisms that
may be available such as, but not limited to, sigqueue(2), etc.
If in doubt: ask.
Makefile
You are responsible for writing a Makefile to compile your code. Typing
"make", "make lockdown" or "make all" should result
in the compilation of all required components with the result being your two
programs (proxy and proxychat). You should
ensure that all required dependencies are specified correctly.
Deliverables
- Source code:
- All source code required to compile and execute your
proxy and proxychat programs
(at a minimum, we'd expect to see a proxy.c that
contains the code for the proxy process, and proxychat.c
containing the code for the chat client program; however, you are
encouraged to organized your code in a modular fashion so obviously
header files or other source code files required for your
implementation should be included).
- Makefile
- The required README file containing descriptions of the
files you are submitting and any notes that may be relevant to
the marker (e.g. compilation and execution instructions, etc.)
- Documentation:
- Brief documentation should include:
- Statement of the problem solved
- Design notes: how you solved the problem (specifically, a
brief description of the solution you developed to solve the
problem---in this case, the design and operation of your
exclusive access and queuing protocol).
- Decisions/assumptions/justifications: state any assumptions
you made in solving the problem and known limitations of
your implementation; justify the design decisions you made in
the previous section (i.e. why a particular approach and not
another?).
- Testing: indicate what methods you have used to ensure the
correct/appropriate operation of your solution.
- Documentation can be provided in PDF or text
format, and only those formats will be accepted. Do not submit
Word documents or any other random things you feel like. The
file should be named A1_docs.pdf or A1_docs.txt
as appropriate.
Electronic submission:
- All of the above, including the documentation and required README
file should be tar'ed, gzip'ed and submitted through the
Moodle system
as described in the
submission guidelines
for the course.
Last Modified: 2012 / 01 / 31