This assignment is intended to introduce you concurrency between processes using semaphores and shared memory in the Unix operating system. It will also give you experience in one kind of Inter-Process Communication (IPC). As before, you are to implement the program described below on any machines you wish. However, specific examples are provided for Unix systems.
You are to write a program that implements a simple "chat" program. Your program will allow multiple users to connect in a live chat session. Users will enter chat messages and have their messages appear on the screens of the other users until a user types "exit". The users will decide on their "handles" and other startup information before the session starts.
A sample chat session follows:
susan 163 wpi=>>chat Hello. Enter your handle: Susan (Ctrl-C pressed) =>> Hi, Joe! Susan: Hi, Joe! (Ctrl-C pressed) =>> I've been playing Quake2 all night. Yowza! Joe: Boy, am I starvin'! Doing all that OS homework makes me hungry. Joe: I'm fixing to go to Micky D's for some grub! Susan: I've been playing Quake2 all night. Yowza! (Ctrl-C pressed) =>> Yeah, that prof Claypool is brutal. Can't wait until the term is over ... then I can go to bed at a normal time or stay up late and play with my new chat program! (Ctrl-C pressed) =>> exit Goodbye.
You will use shared memory as a means for the user process' IPC.
The function calls you need for this are shmget()
,
shmat()
, shmdt()
and shmctl()
.
Read the "man" pages for details on their use. Better yet, see the
code share-mem.c
for an example of how they are used.
You will use semaphores as a means to synchronize the user process'
IPC. The function calls you need for this are semget()
,
semctl()
, and semop()
. Read the "man" pages
for details on their use. Better yet, see the code share-sem.c
for an example
of how they are used.
Each chat process basically reads from the shared memory and
displays new messages on the screen. When a user wants to type in a
new message, s/he hits Ctrl-C
and enter a message. The
chat process then writes this to the shared memory and goes back to
reading and displaying messages.
The below picture depicts one possible architectural solution to your chat program:
In this solution, there is one shared memory chunk for all processes. The mailboxes for each process are logically separate, but are contained in this one chunk. Only one semaphore is required since there is only one chunk of memory, but each process must attach to it so they can use it. The first process that starts creates the shared memory chunk and the semaphores, and subsequent processes attach to the shared memory and semaphores.
Pseudo-code for your program for might look like:
begin attach to shared memory if fails make shared memory make semaphore else attach to semaphore set up signal handler to enter messages while(1) { read from chatroom display any new messages sleep for 1 second } end begin signal handler get input if input is "exit" then cleanup and exit write input to chatroom end
For either solution, it will be helpful to store messages in the mailboxes with structures. Below is an example:
struct message { char from[MAX_LEN]; /* person who wrote message */ char text[MAX_LEN]; /* message length */ }; struct mailbox { int count; /* count of messages */ struct message msg[MAX_MSG]; /* array of messages */ };
To make things a bit easier, you can assume:
See the Samples on the
course Web page for signal()
examples.
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 by us), 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.
claypool 85 ccc6=>>ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000bc5 655360 claypool 666 80 0 ------ Semaphore Arrays -------- key semid owner perms nsems status 0x00000bc5 32768 claypool 666 1 ------ Message Queues -------- key msqid owner perms used-bytes messages claypool 86 ccc6=>>ipcrm shm 655360 resource(s) deleted claypool 87 ccc6=>>ipcrm sem 32768 resource(s) deleted claypool 88 ccc6=>>ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status ------ Semaphore Arrays -------- key semid owner perms nsems status ------ Message Queues -------- key msqid owner perms used-bytes messages
You might try the utility ipckill
which uses a
brute-force approach to delete all your allocated resources (this
version works for Linux systems only).
For a nicer interface, you have the option of using 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 doing a chat window, 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:
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.
Here is a short list of quick tips when you use curses:
initscr()
,
cbreak()
, noecho()
at the beginning of your
program.
mvwaddch()
.
To add a string use mvwaddstr()
.
mvwinch()
.
y
coordinate goes before the
x
coordinate in most curses calls.
For more detailed information on curses
you might try:
Doing a "man curses
" at the Unix prompt (of course)
Checking out the NCURSES FAQ
Reading Writing Programs with NCURSES, by Eric S. Raymond and Zeyd M. Ben-Halim
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 chat program (using curses) # CFLAGS = -Wformat -Wall LIBFLAGS = -lncurses CC = gcc all: chat chat: chat.c $(CC) $(CFLAGS) chat.c -o chat $(LIBFLAGS) clean: /bin/rm -rf *.o core chat
Answer the following questions when you turn in your project:
Please include a README file giving some project information. The main information I'd like you to have from the documentation standard is: author(s), date, project id, language, OS dependencies, description and building information. Do note, however, that this information does not take the place of normal comments in your code! These comments are to make it easier to assess a grade if there are difficulties in the program.
Here is a sample of the information you should have:
Author: Mark Claypool Date: 7/01/05 Project ID: Project 2 CS Class: CS3013/502 Programming Language: C OS/Hardware dependencies: Unix, gcc Problem Description: This program implements a chat session that does blah, blah, blah How to build the program: "make"
To hand in project 2:
Be sure the answers to the Questions are in a file called "questions.txt" in the directory with your project code.
Then, make a tar-ball to submit your files:
cd my-dir-with-proj2 rm all-files-not-to-be-turned-in cd .. tar cvf proj2.tar my-dir-with-proj2
Attach them to an email them to me with the subject "loginname-project2"
Send all questions to claypool at cs.wpi.edu.