Assignment 10: Advanced IPC: Message Queues and Signal Trapping

I. Objective & Theoretical Framework

In any operating system, different processes need to communicate with each other and do not function in isolation. This laboratory explores two advanced mechanisms for Interprocess Communication (IPC): Signals and Message Queues.

1. Signal Passing and Trapping: Signals are generated in response to an event (such as pressing a keyboard interrupt). Processes can use the signal() system call to trap system-generated signals at specified occurrences of events or to communicate between two user programs. The kill() system call is used to send a signal to a specific process.

2. Message Passing: Unlike signals which have predefined values, messages are like text conversations between processes. Before exchanging messages, a message queue must be created using the msgget() system call, which acts like a mailbox where messages are queued up for delivery. The msgsnd() and msgrcv() functions are used to transmit and read these custom data structures.

II. Prerequisite Knowledge & Resources

III. Starter Code & Partial Implementations

The following skeleton code demonstrates how to trap the SIGINT signal (usually triggered by ctrl-c) to execute a custom user-defined function rather than terminating the program.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

// User-defined function to handle the signal
void custom_handler(int signalno) {
    printf("\nYou pressed Ctrl-C! Captured signal: %d\n", signalno);

    // Note: In older UNIX systems, pressing the key twice terminates the program
    // because the function is cleared from memory after execution.
    // A recursive signal() call loads it back.
    signal(SIGINT, custom_handler);
}

int main() {
    printf("Press Ctrl-C...\n");

    // Trapping the SIGINT signal and routing it to custom_handler
    signal(SIGINT, custom_handler);

    // Loop forever to keep the process running
    for(;;) {
        sleep(1);
    }
    return 0;
}

IV. Step-by-Step Task List

  1. Custom Error Handling: Write a program that deliberately attempts an illegal mathematical operation (e.g., integer division by zero). Use the signal() function to trap the resulting SIGILL or SIGFPE signal and invoke a custom error handler that prints a graceful error message before exiting, rather than crashing the program.

  2. Process-to-Process Signaling: Write a program where a parent process fork()s a child. Have the child process trap SIGINT. Meanwhile, have the parent sleep for 2 seconds and then use the kill(pid, SIGINT) system call to send the signal directly to the child process.

  3. Message Queue Creation (Sender): Create a program named send.c.

  • Use msgget() with flags IPC_CREAT | 0666 to create a message queue.

  • Define a structure containing a long mtype (priority) and a char mtext[25] (the text message).

  • Initialize the text with “Good Morning World!!\n” and use msgsnd() to push it to the queue.

  1. Message Queue Retrieval (Receiver): Create a program named receive.c.
  • Access the same queue using msgget().

  • Use msgrcv() to pull the message with the matching priority from the queue and print it to the terminal.

V. Common Pitfalls & Debugging Strategies

VI. Real-World Case Study

Message queues are the fundamental precursors to modern message-broker software like Apache Kafka and RabbitMQ. In enterprise microservice architectures, different services (e.g., an inventory server and a payment processing server) must communicate reliably. Instead of direct, synchronous HTTP calls that can fail if one server goes down, they send structured payloads to a persistent queue. The receiver pulls and processes the message when it is ready, ensuring zero data loss during high-traffic spikes.

VII. Advanced Variant Tasks


VIII. Resources & Further Reading

SDB Watermark