Understanding IPC Objects
System V IPC provides three main inter-process communication mechanisms known as IPC objects: message queues, shared memory, and semaphore arrays. These objects exist in kernel space and are globally accessible through unique identifiers, enabling communication between unrelated processes.
IPC objects persist in the system untill explicitly deleted or the system is shut down. You can manage them using system commands:
ipcs- Display all IPC objectsipcs -q- Show message queuesipcs -m- Show shared memory segmentsipcs -s- Show semaphoresipcrm -q msqid- Remove message queue with ID msqid
Message Queue Fundamentals
Key Characteristics
Message queues are linked lists of messages stored in memory and managed by the kernel:
- Messages have specific types and structured formats
- Random access to messages by type, not just FIFO
- Multiple processes can read from or write too the same queue
- Reading removes messages from the queue
- Each queue has a unique system-wide identifier
- Queues persist until system reboot or manual deletion
System Limits
Typical System V message queue constraints include:
- Maximum message size: 8KB
- Maximum queue capacity: 16KB
- Maximum system queues: 1609
- Maximum total messages: 16384
Generating IPC Keys
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path, int project_id);
The ftok function generates a unique key from a file path and project identifier (0-127).
Example: Key Generation
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main() {
key_t queue_key;
queue_key = ftok(".", 100);
if (queue_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
printf("Generated key: 0x%x\n", queue_key);
return 0;
}
Message Queue Operations
Creating a Message Queue
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int flags);
Creates or accesses a message queue. Use IPC_CREAT | 0666 for creation with read/write permissions.
Example: Queue Creation
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main() {
key_t queue_key;
int queue_id;
queue_key = ftok(".", 100);
if (queue_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
queue_id = msgget(queue_key, IPC_CREAT | 0666);
if (queue_id == -1) {
perror("msgget failed");
exit(EXIT_FAILURE);
}
printf("Queue ID: %d\n", queue_id);
system("ipcs -q");
return 0;
}
Sending Messages
int msgsnd(int queue_id, const void *message,
size_t msg_size, int flags);
Messages require a specific structure with a long type field followed by content.
Message Structure
typedef struct {
long msg_type;
char msg_content[128];
} MessageBuf;
#define CONTENT_SIZE (sizeof(MessageBuf) - sizeof(long))
Example: Sending Messages
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUFFER_SIZE 128
typedef struct {
long msg_type;
char msg_content[BUFFER_SIZE];
} MessageBuf;
#define CONTENT_SIZE (sizeof(MessageBuf) - sizeof(long))
int main() {
key_t queue_key;
int queue_id;
queue_key = ftok(".", 100);
if (queue_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
queue_id = msgget(queue_key, IPC_CREAT | 0777);
if (queue_id == -1) {
perror("msgget failed");
exit(EXIT_FAILURE);
}
MessageBuf msg1 = {1, "First message"};
MessageBuf msg2 = {2, "Second message"};
if (msgsnd(queue_id, &msg1, CONTENT_SIZE, 0) == -1) {
perror("msgsnd failed");
exit(EXIT_FAILURE);
}
if (msgsnd(queue_id, &msg2, CONTENT_SIZE, 0) == -1) {
perror("msgsnd failed");
exit(EXIT_FAILURE);
}
return 0;
}
Receiving Messages
ssize_t msgrcv(int queue_id, void *message,
size_t msg_size, long msg_type, int flags);
The msg_type parameter controls message selection:
- 0: Read messages in FIFO order
- >0: Read first message of specified type
- <0: Read first message with type ≤ |msg_type|
Example: Receiving Messages
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUFFER_SIZE 128
typedef struct {
long msg_type;
char msg_content[BUFFER_SIZE];
} MessageBuf;
#define CONTENT_SIZE (sizeof(MessageBuf) - sizeof(long))
int main() {
key_t queue_key;
int queue_id;
MessageBuf received_msg;
queue_key = ftok(".", 100);
if (queue_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
queue_id = msgget(queue_key, IPC_CREAT | 0777);
if (queue_id == -1) {
perror("msgget failed");
exit(EXIT_FAILURE);
}
// Receive message of type 2
if (msgrcv(queue_id, &received_msg, CONTENT_SIZE, 2, 0) == -1) {
perror("msgrcv failed");
exit(EXIT_FAILURE);
}
printf("Received: %s\n", received_msg.msg_content);
return 0;
}
Controlling Message Queues
int msgctl(int queue_id, int command, struct msqid_ds *buf);
Common commands:
IPC_STAT: Get queue statusIPC_SET: Set queue parametersIPC_RMID: Remove queue
Example: Queue Deletion
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main() {
key_t queue_key;
int queue_id;
queue_key = ftok(".", 100);
if (queue_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
queue_id = msgget(queue_key, IPC_CREAT | 0777);
if (queue_id == -1) {
perror("msgget failed");
exit(EXIT_FAILURE);
}
printf("Queue ID: %d\n", queue_id);
system("ipcs -q");
if (msgctl(queue_id, IPC_RMID, NULL) == -1) {
perror("msgctl failed");
exit(EXIT_FAILURE);
}
system("ipcs -q");
return 0;
}