Programming Project: Implement an IPC for Messages in Unix/Linux

As pointed out before, pipes can be used for processes to exchange information, but processes must either wait for pipe contents if using blocking protocol, or repeatedly check for the availability of pipe contents if using nonblocking protocol. Signals can be used as notifications when certain events occur but they do not convey other information. The programming project is for the reader to combine signals and pipes to implement an IPC mechanism for processes to exchange messages. In the project, messages may be defined as fixed length text strings, e.g. a string of 64 chars. Processes do not have to wait for messages. When a process sends a message by writing to a pipe, it sends a signal to the target process to notify it of the arriving message. Responding to the signal, the receiving process can read the message from the pipe without checking or being blocked. The project consists of two parts. Part 1 presents a base code for the reader to get started, which is shown below.

Part 1: Project Base Code

/******* Base Code of Programming Project *******/

#include <stdio.h>

#include <signal.h>

#include <fcntl.h>

#include <string.h>

#define LEN 64

int ppipe[2];  // pipe descriptors

int pid;      // child pid

char line[LEN];

int parent()

{

printf(“parent %d running\n”, getpid());

close(ppipe[0]); // parent = pipe writer

while(1){

printf(“parent %d: input a line : \n”, getpid());

fgets(line, LEN, stdin);

line[strlen(line)-1] = 0; // kill \n at end

printf(“parent %d write to pipe\n”, getpid());

write(ppipe[1], line, LEN); // write to pipe

printf(“parent %d send signal 10 to %d\n”, getpid(), pid);

kill(pid, SIGUSR1);   // send signal to child process

}

}

void chandler(int sig)

{

printf(“\nchild %d got an interrupt sig=%d\n”, getpid(), sig);

read(ppipe[0], line, LEN); // read pipe

printf(“child %d get a message = %s\n”, getpid(), line);

}

int child()

{

char msg[LEN]; int parent = getppid();

printf(“child %d running\n”, getpid());

close(ppipe[1]); // child is pipe reader

signal(SIGUSR1, chandler); // install signal catcher

while(1);

}

int main()

{

pipe(ppipe);

pid = fork();

if (pid) // parent

parent();

else

child();

}

In the base code, the parent process creates a pipe, forks a child and behaves as a writer to the pipe. The child is the pipe reader. It installs a SIGUSR1 signal catcher and then loops. When the parent runs, it gets an input string, writes the string to the pipe as a message and sends a SIGUSR1 signal to the child, which receives and displays the message in the signal catcher. Then it repeats the loop. Figure 6.3 shows the sample outputs of running the base code program.

Fig. 6.3 Sample outputs of
project base code

Programming Project Part 2 Modify the project base code to implement the following.

  •  Send Reply: After sending a message the sender waits for a reply before sending the next message.

Correspondingly, after receiving a message the receiver sends a reply back to the sender. For example, the reply may be the original message converted to uppercase.

  •  Time Stamp: When sending a message or reply, add a time stamp to the message by the current time. After receiving a reply, compute and show the message round trip time.
  •  Timeout and Resend: Assume that the parent process is unreliable. After receiving a message, it may decide randomly not to send a reply, which would cause the sender to wait indefinitely. Add timeout and resend as follows. After sending a message, set a real-time mode interval timer of 10 msec. If received a reply before the timer expires, cancel the timer. Otherwise, resend the message again (with a new time stamp) until a reply is received.

HINT: Chap. 5 on interval timer signals.

Source: Wang K.C. (2018), Systems Programming in Unix/Linux, Springer; 1st ed. 2018 edition.

Leave a Reply

Your email address will not be published. Required fields are marked *