You can feel the tension in your neck as you furiously cram for your mid-term exams. Your eyes burn as you pore over pages of "required" reading, trying to soak up three weeks of material in one night. Your OS prof has been especially brutal, assigning scads of crap that only loosely tied into the class lectures. Ugh, you need a break, but you can't stand the thought of another slice of pizza or any more of your roommate's coffee. Then, suddenly, it hits you. Just the diversion you need ... a rousing game of Micro Pong!
This assignment is intended to introduce you to sharing data between processes with shared memory and synchronizing processes by using semaphores in the Unix operating system. You are to implement the program described below on any CCC Alpha machines (reno, bert, ernie, scooter, wpi ...)
You are to make a two player (one process per player) game of
pong
. Your pong
game must be a typical pong game:
Here is a screen-shot of a possible game in action:
Billy: 8 Marky: 6 | | | | | ===== | | | | | | | | | | | | * | | | | | | | | | | | | | | | | | | ===== | | | | | j- left, k- right, space- stop
(You can try out the above game at:
/cs/cs3013/pong
)
You are free (and encouraged) to be creative with any of the game aspects, including speed, direction, paddle size, characters, keyboard controls, scoring ... you might try some of the suggested game "variations" below.
You will use the curses
(actually,
ncurses
, a freeware implementation of the System V
curses API with some clearly marked extensions) library to
help with the text-based aspects of the program. The
curses
package is a subroutine library for
terminal-independent screen-painting and input-event handling which
presents a high-level screen model to the programmer, hiding
differences between terminal types and doing automatic optimization of
output to change one screen-full of text into another.
For this project, you will likely only need some basic functionality: how to create windows, refresh them, add characters and get input without waiting for a carriage return. The best way of learning curses is by following some examples:
bounce.c
- a
sample program showing a curses "ball" bouncing around a box.
walk.c
-
a sample program showing how to make a person "walk" around a box.
get-key.c
-
a sample program showing how to capture keyboard input.
In order to compile a program that uses curses
you
will need the -lncurses
flag on the command line. Better
yet, put it in a Makefile.
You should use the curses
call:
int nodelay(WINDOW *win, bool bf);to make
getch()
a non-blocking call (you might try
wgetch()
if you are having difficulty making
getch()
non-blocking). In this case, if no input is
ready, getch()
returns ERR
. If disabled
(bf
is FALSE), getch()
then blocks
until a key is pressed.
For more detailed information on curses
you might try:
man curses
" at the Unix prompt
Your pong
game also must:
Use shared memory and a semaphore (or more) to synchronize the two pong games (processes)
Clean-up all resources (shared memory and semaphores) upon termination.
Support each player in a different login window.
You will use shared memory as a means for users to share the game
state information. You will use a semaphore (or more) to synchronize
access to the shared game state by the two pong
processes. The first pong
process started will first try
to attach to the shared memory (or semaphore), assuming the other
pong
program is already running. If the attach fails,
however, pong
will create the shared memory and semaphore
and wait for the other pong
to join the game by
attaching.
Implementation of Unix semaphores and shared memory can be a bit
ugly. To simplify use of shared memory and semaphores in this
assignment, library routines to create and manipulate underlying Unix
primitives have been provided. The source code containing these
primitives is in shm.c
.
A header file containing prototype definitions for
the routines is in shm.h
. This
makefile
can be used
to make your pong
executable file by first building the
shared memory library. You should download Makefile
,
shm.c
and shm.h
to your working directory. The
routines provided in shm.c
are:
/* * shmattach() - Attach to a shared memory segment with the given key. * Return NULL on failure. */ void *shmattach(key_t key); /* * shmcreate() - create the given amount of shared memory and return the * pointer to this memory. Return NULL on failure. We have imposed * a limit of 2048 bytes for the total amount of shared memory created. */ void *shmcreate(key_t key, int cnt /* number of bytes */); /* * shmdelete() - delete the shared memory associated with the given address. * Return -1 on failure, zero on success. */ int shmdelete(void *sbShm); /* * semattach() - attach to the semaphore indicated by key. Return * -1 if error. */ int semattach(key_t key); /* * semcreate() - create a semaphore with the given initial count and the key. * Returns -1 on failure, a semaphore id on success. */ int semcreate(key_t key, int cnt); /* * semwait() - wait on the given semaphore. Return -1 on error, zero on * success. */ int semwait(int sem); /* * semsignal() - signal the given semaphore. Returns -1 on failure. */ int semsignal(int sem); /* * semdelete() - delete the semaphore corresponding to the given sem id. Returns * -1 on failure, zero on success. */ int semdelete(int sem); /* * semcount() - return the count of the given sem id. -1 on failure. */ int semcount(int sem);
Some simple samples showing how to use the above calls:
critical.c
-- critical section without using semaphores example, but with
using shared memory.
critical-sem.c
-- critical section using semaphores example (and with still
using shared memory).
ipckill
-- nifty utility to help clean up resources.
You will need to use Unix signals
to handler
timer interrupts and gracefully clean-up on a Ctrl-C.
See the following samples:
signal.c
-- simple use of a Unix signal handler.
signal-alarm.c
-- using signal to catch alarms.
Note, you must use the signal-alarm handler rather than
usleep()
or busy waiting, the latter will beat on the
CPU.
See the samples section on the course web page for more signal samples.
There is a fixed limit on the number of semaphores and shared
memory segments that can be allocated on the system. These resources
DO NOT automatically get cleaned up when a process exits
abnormally. It is expected that your program will cleanup semaphore
and shared memory segments that your program has allocated before it
exits. To determine if your program terminates without cleaning up
resources use the command ipcs
. This command will list
all message queues (not used in this project), shared memory, and
semaphore resources that have been allocated. To cleanup your
resources use the command ipcrm -m memid -s semid ...
.
The following example illustrates use of these commands:
% ipcs IPC status as of Thu Aug 29 19:52:24 1996 T ID KEY MODE OWNER GROUP QBYTES Message Queues: T ID KEY MODE OWNER GROUP SEGSZ Shared Memory: m 4224 0x278a0000 --rw-rw---- claypool 10122 1024 T ID KEY MODE OWNER GROUP NSEMS Semaphores: s 2800 0x278a0000 --ra-ra---- cew 10122 15 % ipcrm -m 4224 -s 2800 % ipcs IPC status as of Thu Aug 29 19:52:47 1996 T ID KEY MODE OWNER GROUP QBYTES Message Queues: T ID KEY MODE OWNER GROUP SEGSZ Shared Memory: T ID KEY MODE OWNER GROUP NSEMS Semaphores:
You might try the ipckill
utility to aid you in removing these resources. Note, if someone
wants to write a version of ipckill
for Linux, I (and
other Linux users) would greatly appreciate it.
A useful program for maintaining large programs that have been broken into many software modules is make. The program uses a file, usually named Makefile, that resides in the same directory as the source code. This file describes how to ``make'' a number of targets specified. To use, simply type "make" at the command line. WARNING: The operation line(s) for a target MUST begin with a TAB (do not use spaces). See the man page for make and gcc for additional information.
# # Makefile for pong program # CFLAGS = -g LIBFLAGS = -lncurses all: pong pong: pong.c gcc $(CFLAGS) pong.c shm.o -o pong $(LIBFLAGS) shm.o: shm.c shm.h gcc -c shm.c clean: /bin/rm -rf *.o core pong
Out of the number `7
' (Ctrl-G
) as a
character will result in a beep on most terminals. Use it for
simple sound effect enhancements!
Variations: To the basic pong described above, you might consider one or more of the below game variations:
Answer the following questions when you turn in your project:
Would your pong
program work if one person was in
Seattle and the other was here at WPI? Why or why not and in under
what conditions?
Besides using shared memory for the IPC, briefly describe another
way to communicate between the pong
processes.
Would you ever want to allow both pong
processes
to access (read or write) the shared memory? If so, when? How would
you modify your code to allow this?
What may be the troubles with using usleep()
to delay
between ball movements rather than an alarm? Hint: think about
moving your pong to a different machine.
Send all questions to the TA mailing list.