- Overview
- Key Concepts
- Data Structures
- Core Functions
- Usage Examples
- Permissions and Security
- Concurrency and Limitations
- Error Codes and Troubleshooting
System V message queues provide a mechanism for inter-process communication (IPC) that allows processes to exchange data in the form of messages. Unlike pipes or shared memory, message queues offer structured communication with message boundaries preserved and support for message priorities.
- Inter-process communication: Enable data exchange between unrelated processes
- Asynchronous communication: Sender and receiver don't need to be synchronized
- Message ordering: Messages can be retrieved in FIFO order or by type
- Persistence: Message queues persist until explicitly removed or system shutdown
- Client-server applications: Multiple clients sending requests to a server
- Producer-consumer patterns: Data processing pipelines
- Event notification systems: Broadcasting events to multiple processes
- Task queuing: Distributing work among worker processes
- System monitoring: Collecting status information from various processes
A message queue is a kernel-maintained linked list of messages. Each queue is identified by a unique integer key and has associated permissions and attributes.
Messages have a positive integer type that allows selective retrieval:
- Type > 0: Specific message type
- Type = 0: Retrieve first message regardless of type
- Type < 0: Retrieve first message with lowest type ≤ |type|
Message queues use standard Unix permission bits:
- Owner permissions: Read (0400), Write (0200)
- Group permissions: Read (0040), Write (0020)
- Other permissions: Read (0004), Write (0002)
The msqid_ds structure contains message queue attributes:
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd() */
time_t msg_rtime; /* Time of last msgrcv() */
time_t msg_ctime; /* Time of last change */
unsigned long msg_cbytes; /* Current bytes in queue */
msgqnum_t msg_qnum; /* Current number of messages */
msglen_t msg_qbytes; /* Maximum bytes allowed */
pid_t msg_lspid; /* PID of last msgsnd() */
pid_t msg_lrpid; /* PID of last msgrcv() */
};struct ipc_perm {
key_t key; /* Key supplied to msgget() */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short seq; /* Sequence number */
};User-defined message buffer structure:
struct msgbuf {
long mtype; /* Message type (must be > 0) */
char mtext[1]; /* Message data */
};#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);Purpose: Generate a unique key for IPC objects based on a file path and project identifier.
Parameters:
pathname: Path to an existing fileproj_id: Project identifier (only lower 8 bits used)
Return Value:
- Success: Unique key value
- Error: -1 (errno set)
Example:
key_t key = ftok("/tmp/myapp", 'A');
if (key == -1) {
perror("ftok");
exit(1);
}#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);Purpose: Create a new message queue or access an existing one.
Parameters:
key: Unique identifier (from ftok() or IPC_PRIVATE)msgflg: Creation flags and permissions
Flags:
IPC_CREAT: Create queue if it doesn't existIPC_EXCL: Fail if queue already exists (used with IPC_CREAT)- Permission bits (e.g., 0666)
Return Value:
- Success: Message queue identifier (non-negative integer)
- Error: -1 (errno set)
Common Errors:
EACCES: Permission deniedEEXIST: Queue exists and IPC_CREAT|IPC_EXCL specifiedENOENT: Queue doesn't exist and IPC_CREAT not specifiedENOMEM: Insufficient memoryENOSPC: Maximum number of queues exceeded
Example:
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1) {
perror("msgget");
exit(1);
}#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);Purpose: Send a message to the specified message queue.
Parameters:
msqid: Message queue identifiermsgp: Pointer to message buffermsgsz: Size of message data (excluding mtype)msgflg: Control flags
Flags:
IPC_NOWAIT: Don't block if queue is full0: Block until message can be sent
Return Value:
- Success: 0
- Error: -1 (errno set)
Common Errors:
EACCES: Write permission deniedEAGAIN: Queue full and IPC_NOWAIT specifiedEFAULT: Invalid msgp addressEIDRM: Queue was removedEINTR: Interrupted by signalEINVAL: Invalid msqid, mtype ≤ 0, or msgsz < 0ENOMEM: Insufficient memory
Example:
struct {
long mtype;
char mtext[100];
} message;
message.mtype = 1;
strcpy(message.mtext, "Hello, World!");
if (msgsnd(msgid, &message, strlen(message.mtext) + 1, 0) == -1) {
perror("msgsnd");
exit(1);
}#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);Purpose: Receive a message from the specified message queue.
Parameters:
msqid: Message queue identifiermsgp: Pointer to message buffermsgsz: Maximum size of message datamsgtyp: Message type selectionmsgflg: Control flags
Message Type Selection:
msgtyp = 0: First message in queuemsgtyp > 0: First message of type msgtypmsgtyp < 0: First message with lowest type ≤ |msgtyp|
Flags:
IPC_NOWAIT: Don't block if no suitable messageMSG_EXCEPT: Receive first message NOT of type msgtyp (msgtyp > 0)MSG_NOERROR: Truncate message if larger than msgsz
Return Value:
- Success: Number of bytes in message data
- Error: -1 (errno set)
Common Errors:
E2BIG: Message too large and MSG_NOERROR not setEACCES: Read permission deniedEAGAIN: No suitable message and IPC_NOWAIT specifiedEFAULT: Invalid msgp addressEIDRM: Queue was removedEINTR: Interrupted by signalEINVAL: Invalid msqid or msgsz < 0ENOMSG: No message of requested type
Example:
struct {
long mtype;
char mtext[100];
} message;
ssize_t bytes = msgrcv(msgid, &message, sizeof(message.mtext), 1, 0);
if (bytes == -1) {
perror("msgrcv");
exit(1);
}
printf("Received: %s\n", message.mtext);#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);Purpose: Perform control operations on message queues.
Parameters:
msqid: Message queue identifiercmd: Command to performbuf: Pointer to msqid_ds structure (may be NULL for some commands)
Commands:
IPC_STAT: Get queue attributes (buf required)IPC_SET: Set queue attributes (buf required)IPC_RMID: Remove queue (buf ignored)IPC_INFO: Get system limits (Linux-specific)MSG_INFO: Get system information (Linux-specific)MSG_STAT: Get queue info by index (Linux-specific)
Return Value:
- Success: 0 (or positive value for some commands)
- Error: -1 (errno set)
Common Errors:
EACCES: Permission deniedEFAULT: Invalid buf addressEIDRM: Queue was removedEINVAL: Invalid msqid or cmdEPERM: Operation not permitted
Examples:
// Get queue statistics
struct msqid_ds buf;
if (msgctl(msgid, IPC_STAT, &buf) == -1) {
perror("msgctl IPC_STAT");
exit(1);
}
printf("Messages in queue: %lu\n", (unsigned long)buf.msg_qnum);
// Remove queue
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("msgctl IPC_RMID");
exit(1);
}
// Change queue permissions
buf.msg_perm.mode = 0644;
if (msgctl(msgid, IPC_SET, &buf) == -1) {
perror("msgctl IPC_SET");
exit(1);
}Producer (sender.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
struct message {
long mtype;
char mtext[100];
};
int main() {
key_t key;
int msgid;
struct message msg;
// Generate key
key = ftok("/tmp", 'A');
if (key == -1) {
perror("ftok");
exit(1);
}
// Create or access message queue
msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1) {
perror("msgget");
exit(1);
}
// Send messages
for (int i = 1; i <= 5; i++) {
msg.mtype = 1;
snprintf(msg.mtext, sizeof(msg.mtext), "Message %d from PID %d", i, getpid());
if (msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0) == -1) {
perror("msgsnd");
exit(1);
}
printf("Sent: %s\n", msg.mtext);
sleep(1);
}
return 0;
}Consumer (receiver.c):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct message {
long mtype;
char mtext[100];
};
int main() {
key_t key;
int msgid;
struct message msg;
ssize_t bytes;
// Generate key
key = ftok("/tmp", 'A');
if (key == -1) {
perror("ftok");
exit(1);
}
// Access message queue
msgid = msgget(key, 0666);
if (msgid == -1) {
perror("msgget");
exit(1);
}
// Receive messages
while (1) {
bytes = msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);
if (bytes == -1) {
perror("msgrcv");
break;
}
printf("Received: %s (%zd bytes)\n", msg.mtext, bytes);
}
// Clean up
msgctl(msgid, IPC_RMID, NULL);
return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#define MSG_TYPE_REQUEST 1
#define MSG_TYPE_RESPONSE 2
#define MSG_TYPE_ERROR 3
struct message {
long mtype;
int client_pid;
char data[256];
};
// Server function
void server() {
key_t key = ftok("/tmp", 'S');
int msgid = msgget(key, IPC_CREAT | 0666);
struct message msg, response;
printf("Server started, waiting for requests...\n");
while (1) {
// Receive request
if (msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), MSG_TYPE_REQUEST, 0) == -1) {
perror("msgrcv");
continue;
}
printf("Server: Received request from PID %d: %s\n", msg.client_pid, msg.data);
// Process request and send response
response.mtype = MSG_TYPE_RESPONSE;
response.client_pid = getpid();
snprintf(response.data, sizeof(response.data), "Processed: %s", msg.data);
if (msgsnd(msgid, &response, sizeof(response) - sizeof(long), 0) == -1) {
perror("msgsnd");
}
}
}
// Client function
void client() {
key_t key = ftok("/tmp", 'S');
int msgid = msgget(key, 0666);
struct message request, response;
// Send request
request.mtype = MSG_TYPE_REQUEST;
request.client_pid = getpid();
strcpy(request.data, "Hello from client");
if (msgsnd(msgid, &request, sizeof(request) - sizeof(long), 0) == -1) {
perror("msgsnd");
exit(1);
}
// Wait for response
if (msgrcv(msgid, &response, sizeof(response) - sizeof(long), MSG_TYPE_RESPONSE, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("Client: Received response: %s\n", response.data);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s [server|client]\n", argv[0]);
exit(1);
}
if (strcmp(argv[1], "server") == 0) {
server();
} else if (strcmp(argv[1], "client") == 0) {
client();
} else {
printf("Invalid argument. Use 'server' or 'client'\n");
exit(1);
}
return 0;
}Message queues use the standard Unix permission model:
- Read permission: Required for msgrcv()
- Write permission: Required for msgsnd()
- Owner/creator: Can always perform msgctl() operations
- Root: Can access any message queue
- Key generation: Use ftok() with secure paths to avoid key collisions
- Permission setting: Set restrictive permissions (e.g., 0600) for sensitive data
- Cleanup: Always remove queues when done to prevent resource leaks
- Validation: Validate message content and size before processing
- Access control: Check permissions before queue operations
// Secure queue creation
int create_secure_queue(const char *path, int proj_id) {
key_t key = ftok(path, proj_id);
if (key == -1) return -1;
// Create with restrictive permissions
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
if (msgid == -1) return -1;
return msgid;
}
// Safe message sending with timeout
int safe_msgsnd(int msgid, const void *msgp, size_t msgsz) {
// Use IPC_NOWAIT to avoid indefinite blocking
if (msgsnd(msgid, msgp, msgsz, IPC_NOWAIT) == -1) {
if (errno == EAGAIN) {
// Queue full, handle appropriately
return -2;
}
return -1;
}
return 0;
}- Atomic operations: msgsnd() and msgrcv() are atomic
- Multiple readers: Multiple processes can read from the same queue
- Multiple writers: Multiple processes can write to the same queue
- Message ordering: FIFO within each message type
- Blocking behavior: Processes block when queue is full (send) or empty (receive)
Check current limits with:
# View current limits
ipcs -l
# Common limits (vary by system)
cat /proc/sys/kernel/msgmax # Maximum message size
cat /proc/sys/kernel/msgmnb # Maximum queue size
cat /proc/sys/kernel/msgmni # Maximum number of queues- Message size: Smaller messages are more efficient
- Queue depth: Deep queues can impact performance
- Memory usage: Messages consume kernel memory
- Context switching: Blocking operations cause context switches
// Monitor queue usage
void print_queue_stats(int msgid) {
struct msqid_ds buf;
if (msgctl(msgid, IPC_STAT, &buf) == -1) {
perror("msgctl");
return;
}
printf("Queue statistics:\n");
printf(" Messages: %lu\n", (unsigned long)buf.msg_qnum);
printf(" Bytes: %lu\n", buf.msg_cbytes);
printf(" Max bytes: %lu\n", buf.msg_qbytes);
printf(" Last send PID: %d\n", buf.msg_lspid);
printf(" Last recv PID: %d\n", buf.msg_lrpid);
}
// Cleanup function
void cleanup_queue(int msgid) {
struct msqid_ds buf;
// Get current stats
if (msgctl(msgid, IPC_STAT, &buf) == 0) {
printf("Removing queue with %lu messages\n", (unsigned long)buf.msg_qnum);
}
// Remove queue
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("msgctl IPC_RMID");
}
}| Error Code | Description | Common Causes | Solutions |
|---|---|---|---|
EACCES |
Permission denied | Insufficient permissions | Check queue permissions, run as appropriate user |
EAGAIN |
Resource temporarily unavailable | Queue full (IPC_NOWAIT), no message available | Retry later, increase queue size, check queue status |
EEXIST |
File exists | Queue exists with IPC_CREAT|IPC_EXCL | Use different key or remove existing queue |
EFAULT |
Bad address | Invalid pointer | Check buffer addresses and sizes |
EIDRM |
Identifier removed | Queue was deleted | Recreate queue or handle gracefully |
EINTR |
Interrupted system call | Signal received | Retry operation or handle signal |
EINVAL |
Invalid argument | Bad msgid, negative size, zero mtype | Validate all parameters |
ENOMEM |
Out of memory | System memory exhausted | Free memory, reduce message sizes |
ENOENT |
No such file or directory | Queue doesn't exist, bad ftok() path | Create queue first, check file path |
ENOSPC |
No space left on device | System limits exceeded | Increase limits, clean up unused queues |
# List all message queues
ipcs -q
# Show detailed information
ipcs -q -i <msgid>
# Remove specific queue
ipcrm -q <msgid>
# Remove all queues for user
ipcrm -Q <key>#include <errno.h>
#include <string.h>
int safe_msgget(key_t key, int flags) {
int msgid = msgget(key, flags);
if (msgid == -1) {
switch (errno) {
case EACCES:
fprintf(stderr, "Permission denied for key 0x%x\n", key);
break;
case EEXIST:
fprintf(stderr, "Queue already exists for key 0x%x\n", key);
break;
case ENOENT:
fprintf(stderr, "Queue does not exist for key 0x%x\n", key);
break;
case ENOSPC:
fprintf(stderr, "System limit reached\n");
break;
default:
fprintf(stderr, "msgget failed: %s\n", strerror(errno));
}
}
return msgid;
}// Check if queue exists
int queue_exists(key_t key) {
int msgid = msgget(key, 0);
return (msgid != -1);
}
// Get queue size
long get_queue_size(int msgid) {
struct msqid_ds buf;
if (msgctl(msgid, IPC_STAT, &buf) == -1) {
return -1;
}
return buf.msg_qnum;
}
// Wait for queue to be available
int wait_for_queue(key_t key, int timeout_sec) {
int attempts = 0;
int max_attempts = timeout_sec * 10; // Check every 100ms
while (attempts < max_attempts) {
int msgid = msgget(key, 0);
if (msgid != -1) {
return msgid;
}
usleep(100000); // 100ms
attempts++;
}
return -1; // Timeout
}-
Queue Creation Issues:
- Verify file path exists for ftok()
- Check permissions on the file used by ftok()
- Ensure unique project ID
- Verify system limits not exceeded
-
Permission Problems:
- Check queue permissions with
ipcs -q - Verify process runs with appropriate user/group
- Ensure read/write permissions match operations
- Check queue permissions with
-
Message Send/Receive Issues:
- Validate message type (must be > 0 for send)
- Check message size against system limits
- Verify queue isn't full (for non-blocking sends)
- Ensure correct message type for receive
-
Resource Leaks:
- Always clean up queues with msgctl(IPC_RMID)
- Monitor system resources with
ipcs - Implement proper error handling and cleanup
-
Performance Issues:
- Monitor queue depth and message sizes
- Consider using multiple queues for high throughput
- Implement timeouts for blocking operations
- Profile application for bottlenecks
#!/bin/bash
# cleanup_queues.sh - Remove all message queues for current user
echo "Cleaning up message queues..."
# Get list of queue IDs for current user
QUEUES=$(ipcs -q | grep $(whoami) | awk '{print $2}')
if [ -z "$QUEUES" ]; then
echo "No message queues found for user $(whoami)"
exit 0
fi
# Remove each queue
for qid in $QUEUES; do
echo "Removing queue ID: $qid"
ipcrm -q $qid
if [ $? -eq 0 ]; then
echo " Successfully removed"
else
echo " Failed to remove"
fi
done
echo "Cleanup complete"This comprehensive documentation covers all aspects of the System V message queue API, providing both theoretical understanding and practical examples for effective usage.