|
CMSC421
PRINCIPALS OF OPERATING SYSTEMS
Section 0101 |
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 percent 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 submit.
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 turn in four one-page (single spaced, 10 point font)
reports, one on each of the IPC mechanisms used (shared file, shared memory, pipe, socket). These reports should address the
fundamental concepts used for these IPC methods, and information on the data structures managed by the kernel for
implementation.
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 shared memory 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 appropriate side of the pipe. Write the message to the
pipe.
Greatgrandchild Process
The Greatgrandchild will close the appropriate 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 two thirds of the semester's project grade (i.e. twice the first project).
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 following reports listed 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 percent (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.