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
- Linux Signals: In Linux environments, the
ctrl-ckey combination generates aSIGINTsignal trap. For illegal operations like division by zero, the kernel generatesSIGILLorSIGFPE(floating-point exception). - Required Headers:
<signal.h>,<sys/types.h>,<sys/ipc.h>,<sys/msg.h>. - Command Line Tools: You can view existing message queues in the kernel by issuing the command
ipcs -qfrom your console.
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
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 resultingSIGILLorSIGFPEsignal and invoke a custom error handler that prints a graceful error message before exiting, rather than crashing the program.Process-to-Process Signaling: Write a program where a parent process
fork()s a child. Have the child process trapSIGINT. Meanwhile, have the parent sleep for 2 seconds and then use thekill(pid, SIGINT)system call to send the signal directly to the child process.Message Queue Creation (Sender): Create a program named
send.c.
Use
msgget()with flagsIPC_CREAT | 0666to create a message queue.Define a structure containing a
long mtype(priority) and achar mtext[25](the text message).Initialize the text with “Good Morning World!!\n” and use
msgsnd()to push it to the queue.
- 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
Message Truncation Errors: When receiving a message, you must specify the length. If this value is less than the length of the message sent, you will get an error: “msgrcv failed: Arg list too long”. To avoid this, OR the last argument of your
msgrcv()function with the constantMSG_NOERROR, which will instruct the program to ignore the overshot length and simply truncate the output.Kernel Pollution (Dangling Queues): Message queues persist in the operating system’s memory even after your program finishes executing. If you run
send.cmultiple times,ipcs -qwill show multiple messages waiting in the queue. You must either runreceive.cenough times to eat up the stored messages, or use themsgctl()system call with theIPC_RMIDflag to destroy the queue programmatically.Kernel Memory Leaks: Unlike standard variables, message queues survive even if your C program crashes. Use the
ipcs -qcommand to view orphaned queues. You must learn to manually destroy these memory leaks using theipcrm -q [Message_Queue_ID]command from your terminal to keep the OS stable during development.
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
Two-Way Acknowledgment System: Write a pair of programs to simulate a two-way chat. The sender program accepts a string from the command console and sends it as a message to the receiver. The receiver, upon receiving the message, displays it and immediately sends an acknowledgment message back through the queue. The sender must wait to display “Acknowledgment received” before accepting the next user input.
Semaphore Data Structures: Expand your IPC knowledge by exploring Semaphores, which help synchronize several processes that access a common resource. Write a program utilizing
semget()to create a set of semaphores, and usesemctl()with theSETVALandGETVALcommands to set and retrieve integer values from the semaphore. Retrieve the PID of the process that set the value by passing theGETPIDconstant.Asynchronous Chat Application: Using POSIX Message Queues, build a two-way command-line chat app. Open two independent terminal windows running client binaries. Use
msgget(),msgsnd(), andmsgrcv()to allow real-time communication between the two windows, relying entirely on kernel-level queues rather than network sockets. Ensure you implement a graceful exit hook (SIGINTtrap) that destroys the queue upon closing.
VIII. Resources & Further Reading
- Beej’s Guide to Unix IPC: This is the undisputed best internet resource for learning C-based interprocess communication. Read Beej’s Guide to Unix Interprocess Communication—specifically the chapters on Signals and Message Queues.
- Linux Signal Management: Review the official documentation for signal(2) and kill(2).
- System V IPC: Review the manual pages for msgget(2) and msgctl(2) to ensure you understand how to gracefully create and destroy kernel memory queues.