CMPS 3600 Lab-4 - IPC Shared Memory & Message Queues

GOAL: Introduce System V interprocess communication (IPC) message queues and shared memory

resources

BACKGROUND. SysV Interprocess communication (IPC) is the standard (portable) way of communicating among related (parent and child) and unrelated processes. This lab introduces *two* of the three components of the SysV IPC suite--message queues and shared memory. The two processes that communicate will be a parent and its spawned child. To use SysV IPC, msg queues and shared memory add these headers:

        #include <sys/ipc.h>
        #include <sys/msg.h>
        #include <sys/shm.h>
The third service in the SysV IPC suite is semaphores. If multiple processes reference the same memory location the coder you must control access by synchronization. Synchronization is implemented with semaphores. Semaphores will be covered oon in our course. This lab will use shared memory for synchronization.

Read these man pages before starting:

        $ man shmget   # shmget is get a shared memory segment 
        $ man shmat    # shmat is attach to a shared memory segment 
        $ man shmctl   # shmctl is shared memory control operations 
        $ man ftok     # ftok is generate a SysV IPC key  
In Lab-4 you will write a program on odin 3600/4/rvlab4.c using SysV IPC shared memory and SysV IPC message queues. You will also include POSIX signal handling using signal sets in your program. Copy these files into your account:
to copy from your Odin account:
   cd
   cd 3600/4
   cp /home/fac/gordon/public_html/3600b/examples/4/* .
Carefully read the documentation in each file. Execute and run each file. When you are comfortable with the code, proceed. You will need to manually remove your IPC objects if things go wrong.
      $ ./cleanipc.sh {username} # this script will cleanup your IPC objects 
      $ ipcs -c username         # shared objects created by you should be gone
Use the commands above to avoid showing up on the "wall of shame". :)

If things go well your objects should be removed within your rvlab4.c program.
While testing ALWAYS REMOVE YOUR IPC OBJECTS before executing your code again.

LAB INSTRUCTIONS.

Please do your work on Odin in folder 3600/4/
Name your program: 3600/4/rvlab4.c

BEFORE THE FORK...

STEP 1. Generate an IPC key and open a log file.

To generate an IPC key, create a file named 'foo' in the directory in which you compile and execute your programs.

   $ cd
   $ cd 3600/4
   $ touch foo

You may also do vi foo and put stuff in the file.

For consistency throughout the rest of the course, we will use 'foo' to generate the IPC key.

Open a file for writing named log. Only the child will write to this file but you should open it before the fork.

STEP 2. Create a shared memory segment to hold at least one integer.

You may create a shared memory segment larger than one integer.
You may also create a shared memory segment to hold a struct, and store your value there.
That is your choice.

You will use the IPC key you just created. The source shared_mem.c communicates between a parent and a forked child using a shared memory segment large enough to hold an integer. Source shared_mem.c has all the code you need to implement shared memory - you just need to take what you need and leave what you don't behind. You can either copy shared_mem.c to rvlab4.c and delete/modify the existing code or start rvlab4.c from scratch. In either case, understand each and every line of code for shared memory that you copy. Do not use any code you do not understand! The system call to create the shared memory segment returns a shared memory id.

Create the segment. Attach to the shared memory segment. Initialize its value to 0. After a fork(2) the child inherits the attached shared memory segment so you will not need to attach again in the child.

STEP 3. Get a message queue & fork(2) a child

Use the same IPC key you have already created to get a message queue. Create the structure needed to send and receive messages (the child and the parent will use the same message structure and type of the message). You will be sending a message from parent to child after the fork. Look in msg_send.c for the code needed to get a message queue using the call msgget(2).

Message queues are designed to allow communication between unrelated processes. The only thing the child and the parent need in order to use the same message queue is the same IPC key. The only way for this to happen is to generate the IPC key before the fork since after the fork the child inherits the value of the IPC key.

Call fork(2).

IN THE CHILD...

STEP 1. Wait for shared memory to change, then write integer value to log.

The parent will modify the shared memory segment to something other than zero but the child does not know when this might happen. The child must loop continuously, checking the value until it changes from zero. This behavior is called a busy-wait. Not always the best way to code, but good for this lab exercise. After the value changes, the child writes the shared memory value to its log file.

busy-wait:
Stay in an infinite loop, constantly checking for something to happen.
When it does happen, immediately break out of the loop.
A short usleep() call should be placed inside the busy-wait, for this lab.

For example:

usleep(1000);  /* sleep for 1-thousandth of a second */

With so many student programs running at once on Odin, the usleep can help the OS run a little more efficiently.

STEP 2. Write mesg from message queue to log.

The child blocks on the queue until the parent sends a message. The code to do this is in msg_rcv.c. The child grabs the message and writes its contents to log.

STEP 3. Exits

The child closes log, detaches from shared memory and exits with status code 0. Note that the child does not clean up IPC objects. It is safer for the parent to do this after the wait.

IN THE PARENT...

STEP 1. Write user input to shared memory

The parent prompts the user to enter a 2-digit integer using the write() call; e.g.,

  Enter a 2-digit number:
The prompt is a write call:
  write(1, prompt, strlen(prompt));
The parent reads the integer using read():
 read(0, buf, 4);  // 0=stdin; 4 is how many characters you want the user to type 
The read of user input can be tricky, so test your code. These calls are in readwrite.c in the examples from last week. You should always clear out the buffer before you do a read:
memset(buf, 0, BUFSIZE);

Since the integer will come in as a string it must be converted to an integer using atoi. The parent then writes the integer to its shared memory segment.

STEP 2. Send user input as a message to the queue

The parent prompts the user to enter a word using write(). The parent reads the word using read(2). The parent constructs a message that holds this word and sends the message to the queue. The code to do this is in msg_send.c.

STEP 3. After child terminates remove IPC objects

The parent calls wait(2), grabs the child's exit code and writes the code to stdout. After that the parent cleans up all IPC objects.

To save time when testing your code create an input file that holds user input:

Name your file: myinput
The contents of your myinput file could look like this for example:
   86
   fourty-six

Note for the myinput file: When reading your 2-digit number from the command-line, the read statement will essentially discard blank characters to the right of the digits. When reading from your input file, the read statement has more data to work with, such as the characters on the second line of your file. Your first read statement might grab some of the characters from your second line. Adjust the "count" argument of the read function calls to control this.
Sample run...
$ ./lab4
Enter a two-digit number: 76
Enter a word: Systemize
Child exited with status code 0

$ cat log
76
Systemize
$ ./lab4 < myinput
Enter a two-digit number: Enter a word: Child exited with status code 0

$ cat log
86
fourtysix
Sample run (the order of your system calls may differ):
$ strace -f -e trace=ipc ./lab4 < myinput

shmget(0x1102af98, 4, IPC_CREAT|0666)   = 911605760
shmat(911605760, 0, 0)                  = ?
msgget(0x1102af98, IPC_CREAT|0666)      = 297172992
Process 9356 attached
[pid  9355] msgsnd(297172992, {1, " \nfourty-six\n\0\0\0\0\0\0\0\0\0"...}, 112, 0) = 0
[pid  9356] msgrcv(297172992, Process 9355 suspended
{1, " \nfoutry-six\n\0\0\0\0\0\0\0\0\0"...}, 112, 0, 0) = 112
[pid  9356] shmdt(0x7fac33f01000)       = 0
Process 9355 resumed
Process 9356 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
shmdt(0x7fac33f01000)                   = 0
msgctl(297172992, IPC_RMID, 0)          = 0
shmctl(911605760, IPC_RMID, 0)          = 0

$ cat log
86
fourty-six

$ ipcs -c | grep {username}    # should display nothing

What to turn in...

These files will be collected from your Odin 3600/4 folder
at the end of lab, 12:30pm.

     3600/4/rvlab4.c
     3600/4/Makefile
     3600/4/foo
     3600/4/myinput
Please don't change permissions or create new directories in your 3600/
Try to finish this lab during lab class.

If you do not finish...
  1. Make sure your rvlab4.c program compiles and runs.
  2. No compile warnings or errors.
  3. No IPC components left on the wall-of-shame.
  4. No lab4 or a.out executables still running.
  5. Finish at least the shared memory portion.