|
CMSC421
PRINCIPALS OF OPERATING SYSTEMS
Section 0201/0301
|
CSEE
UMBC
|
Programming Project #2 -- Interprocess Communications (IPC)
Assigned: 22 April 2002
Due: 12 May 2002 at 11:59 PM
5 May 2002 at 11:59 PM for 10 per cent extra credit
Goals
This project will give you experience and an understanding
of the different form of IPCs.
We assume that you have installed linux, however, this can
be done on the Linux machines in the labs. If (or when)
you do this on the UMBC computers, please use the systems in
the lab and reboot them into Linux. There can be problems
if the the IPCs (especially shared memory and sockets)
are not cleaned up properly when you are finished with them.
By using the lab machines in Linux, there is no problem, since
that machine will be rebooted before there is a problem.
[This will also gain the approval of OIT! Since they have been
extremely helpful, I want to stay in their good graces. =:-) ]
For this project, there are many examples for you to examine
in the book:
Advanced Programming in the UNIX Environment
by W. Richard Stevens
Pay special attention to Chapters 4, 8, 14, and 15.
Make sure you read and follow every detail on this page for
the project. No doing something will lower your grade!
Mechanics, and what to hand in
The project is to be done by each individual, not groups.
You will need to hand in all of the code you create or modify,
a screen capture showing your results, any makefiles,
and documentation . You are to create a zip file that contains
everything that you are turning in and submit only the single
zip file using the blackboard system available
on the GL cluster.
Do not send your executables. The TA will rebuild them.
Your design documentation, typically 1-2 pages for a project of
this size, should include the basic design of your software.
You must also trun in four one-page reports, one page each on
the four IPC mechanisms used (shared memory, pipe, shared file, and
socket). These reports should address the fundamental concepts used
for these IPC methods, and information on the data structures managed
by the kernel for implementations.
Version of the Kernel and Linux to Use
This project is to be done on Linux, version does not matter,
because this will work all all distributions and kernels.
Specifics
This project will require that you have processes create child
process and communicate between these processes. In one case,
the process will be on a remote computer.
There will be a twenty percent penalty for not implementing
the sockets and using the remote host! Since I am
providing a working model for you to modify, this will be
the easiest part of the project. (See chapter 15 in
Advanced Programming in the UNIX Environment for this.)
You must use ANSI C for this project. I do not want any language
extensions (things not in the ANSI C standard) used on this
project.
Parent Process
The first process (I'm calling the parent) will create a child.
After the child is created, you must get a message (up to 80
characters in length plus a termination byte) from the
user. Then create a file. Typical UNIX/Linux systems have a
directory of /tmp which is used for temporary file and this
directory is emptied when the system is booted. The name of the
file should be the combination of the string "CMSC421_" and the
PID of the Child. Write the PID of the parent and the message
to the file. Close the file.
Child Process
Since we have no way to synchronize these processes, the Child
must sleep for a period of time to allow the parent to finish and
close the file. Then the Child will have to get its own PID,
rebuild the filename and open the file. Read all the contents
into a buffer and close the file.
Create shared memory and build a message to be put into the
shared memory. The message will content the PID of the Child
as well as the entire contents of the file. (That means you
now have to PIDs and the original user's message.) Write this
into the shared memory. Start the Grandchild process.
Grandchild Process
Read the message from the into a buffer and build a new message
using the PID of the Grandchild. Open a pipe. Notice there are
actually two "files", one for input and one for output. Create
the Greatgrandchild. Close the input side of the pipe. Write the
message to the pipe.
Greatgrandchild Process
The requirements will be change!!
The Greatgrandchild will close the output side of the pipe and
read the message from the Grandchild. Then add the first PID to
the message At this time, the
Greatgrandchild must start a new process using
one of the calls to the exec family. The new program will
act as a client of a process running on another host. The
Greatgrandchild will add its second PID to the message and send it to
the remote host. The remote server will add its PID to the message
and send it back. The Greatgrandchild will use the shared
memory created by the Child and write the message to the shared
memory.
I want to make sure that every one is using a unique socket number,
so use a '4' plus the last four digits of your SSN. (If that does
not get a unique socket number, change the '4' to a '5'.)
Greatgreatgrandchild Process
The Greatgreatgrandchild will read the message from shared memory
and write it out in the following format.
Greatgreatgrandchild's PID: xxxxxx
Remote Server's PID: xxxxxx
Greatgrandchild's first PID: xxxxxx
Greatgrandchild's second PID: xxxxxx
grandchild's PID: xxxxxx
Child's PID: xxxxxx
Parent's PID: xxxxxx
Message from user:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Helpful Hints
- You will have to write several small programs. Each
program will act independently and communicate only in
the ways specified.
- Design the necessary data structures so that the
Greatgreatgrandchild will receive all of the necessary data.
There are six PIDs that must be passed along. The PIDs
can be stored in binary form (pid_t) or in ASCII form.
- Some of the details on how to implement this project have
not been specified. You are to select an implement that
will meet the requirements, which means you should select
one that you understand. Document all instructions.
- To be portable, make sure that you use the standard data
types in unistd.h, such as pid_t, size_t, etc.
- To get started, you should read your text's discussion of sys
calls, and then the man pages for fork, getpid, execve, execvp,
execl, dup, dup2, wait, pipe, perror, read, gets, write, and
for any string functions you might want to use. ("man string"
will bring up a summary page on the basic string functions.)
- The functions execve and execvp take an array of strings as
their arguments. This array is similar to the argv[] array
passed to C programs. When you create this array, it is
important that the end of the array is marked with a null
pointer (Not a null string!); for example if there are k
string pointers in your array args, you would set args[k]=0.
- You might need some or all of the following "include" files.
Based on what you are doing, you might need even more.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
- You should develop your code in a series of stages:
- Pipes and forking are probably the most difficult part of this
project because until you try them, it is hard to correctly
visualize what is happening.
- You should be able to do this project on any system that is
POSIX compatible, including LINUX, SGIs and Sun
workstations. However, the "official" platform for this
project is a Linux platform.
- You will need two windows when you execute this project.
The first one will be to run the project. The other
one will be to start the remote server process. This
must be done before you run the project locally. Make
when you are done, that you kill the process. (Make
sure the remote server is running before you run the
project.
- Running the project will consist of two steps, starting the
remote server and starting the Parent. Your code will
start the other processes when appropriate.
Socket Examples
For this project, use the same number for the port and the socket.
Server (remote) source
/*
** server.c -- a stream socket server demo from Beejs Networking Guide at http://www.ecst.csuchico.edu/~beej/guide/net/#bind
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define MYPORT 3490 /* the port users will be connecting to */
#define BACKLOG 10 /* how many pending connections queue will hold */
main()
{
int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector's address information */
int sin_size;
/* Creating a socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
/* INET information */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(MYPORT); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* automatically fill with my IP */
bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */
/* Binding the socket to the port 3490 */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
/* Listiening for incoming connections */
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
while(1) { /* main accept() loop */
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
if (!fork()) { /* this is the child process */
if (send(new_fd, "Hello, world!\n", 14, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); /* parent doesn't need this */
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up all child processes */
/* Closing sockets important to avoid address already in use error */
close(sockfd);
}
}
Client (local) source code
/*
** client.c -- a stream socket client demo from Beejs Networking guide
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 3490 /* the port client will be connecting to */
#define MAXDATASIZE 100 /* max number of bytes we can get at once */
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in their_addr; /* connector's address information */
if (argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
/* Getting INET information of the server */
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
perror("gethostbyname");
exit(1);
}
/* Creating the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
/* Setting the host information */
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(PORT); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
/* Connecting to the server socket */
if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
perror("connect");
exit(1);
}
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
return 0;
}
Grading the Project
This project is worth 300 points.
The grading for the project will be as follows: 40% design, 60%
implementation. We have structured the grading in this way to
encourage you to think through your solution before you start
coding.
The design portion of the design portion of the documentation
must include the four page report that was mentioned above.
The implementation portion of
the grade considers whether you implemented your design, ran
reasonable test cases, and provided documentation that the TA
could understand. Part of being a good computer scientist is
coming up with simple designs and easy to understand code;
a solution which works isn't necessarily the best that you can
do. Another critical success factor is ability to communicate!
Thus, part of the design and implementation grade will
be based on whether your solution is elegant, simple, and easy
to understand.
You must following the Indentation
Standards and the Coding Standards.
This will be a part of the documentation grade!
As an extra incentive, there is a ten per cent (10%) extra credit
if you have it turned in by 5 May at 11:59 pm.
Points to Ponder
- When the child is created, it gets a PID assigned to it. What is the PID of the
program that the child executes? Should it change or not? Did it change?
- What is the size (in bytes) of the pipe? Is it the same size on all platforms?
Why?
- What are the ramifications of redirecting STDIN and STDOUT?
- What is the problem of using gets()?
Rules for Collaboration
This is a pretty straightforward project with a small amount
of code to be written. We therefore DO NOT allow
you to discuss solution strategies
with others. If you do not do this project yourself, then you
are cheating. No one should see your code and you should not
see any one else code! Please recall
that academic dishonesty will be sternly dealt with.