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

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

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.

UMBC CMSC421