Skip to content

Instantly share code, notes, and snippets.

@alekxeyuk
Created August 9, 2025 13:24
Show Gist options
  • Select an option

  • Save alekxeyuk/bd6af2f68dcf84efaa52cdaf1d0f8fab to your computer and use it in GitHub Desktop.

Select an option

Save alekxeyuk/bd6af2f68dcf84efaa52cdaf1d0f8fab to your computer and use it in GitHub Desktop.

System V Message Queue API Documentation

Table of Contents

  1. Overview
  2. Key Concepts
  3. Data Structures
  4. Core Functions
  5. Usage Examples
  6. Permissions and Security
  7. Concurrency and Limitations
  8. Error Codes and Troubleshooting

Overview

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.

Purpose

  • 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

Typical Use Cases

  • 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

Key Concepts

Message Queues

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.

Message Types

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|

Permissions

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)

Data Structures

msqid_ds Structure

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() */
};

ipc_perm Structure

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 */
};

msgbuf Structure

User-defined message buffer structure:

struct msgbuf {
    long mtype;       /* Message type (must be > 0) */
    char mtext[1];    /* Message data */
};

Core Functions

ftok() - Generate IPC Key

#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 file
  • proj_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);
}

msgget() - Create or Access Message Queue

#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 exist
  • IPC_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 denied
  • EEXIST: Queue exists and IPC_CREAT|IPC_EXCL specified
  • ENOENT: Queue doesn't exist and IPC_CREAT not specified
  • ENOMEM: Insufficient memory
  • ENOSPC: Maximum number of queues exceeded

Example:

int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1) {
    perror("msgget");
    exit(1);
}

msgsnd() - Send Message

#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 identifier
  • msgp: Pointer to message buffer
  • msgsz: Size of message data (excluding mtype)
  • msgflg: Control flags

Flags:

  • IPC_NOWAIT: Don't block if queue is full
  • 0: Block until message can be sent

Return Value:

  • Success: 0
  • Error: -1 (errno set)

Common Errors:

  • EACCES: Write permission denied
  • EAGAIN: Queue full and IPC_NOWAIT specified
  • EFAULT: Invalid msgp address
  • EIDRM: Queue was removed
  • EINTR: Interrupted by signal
  • EINVAL: Invalid msqid, mtype ≤ 0, or msgsz < 0
  • ENOMEM: 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);
}

msgrcv() - Receive Message

#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 identifier
  • msgp: Pointer to message buffer
  • msgsz: Maximum size of message data
  • msgtyp: Message type selection
  • msgflg: Control flags

Message Type Selection:

  • msgtyp = 0: First message in queue
  • msgtyp > 0: First message of type msgtyp
  • msgtyp < 0: First message with lowest type ≤ |msgtyp|

Flags:

  • IPC_NOWAIT: Don't block if no suitable message
  • MSG_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 set
  • EACCES: Read permission denied
  • EAGAIN: No suitable message and IPC_NOWAIT specified
  • EFAULT: Invalid msgp address
  • EIDRM: Queue was removed
  • EINTR: Interrupted by signal
  • EINVAL: Invalid msqid or msgsz < 0
  • ENOMSG: 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);

msgctl() - Control Operations

#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 identifier
  • cmd: Command to perform
  • buf: 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 denied
  • EFAULT: Invalid buf address
  • EIDRM: Queue was removed
  • EINVAL: Invalid msqid or cmd
  • EPERM: 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);
}

Usage Examples

Example 1: Simple Producer-Consumer

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;
}

Example 2: Multi-Type Message System

#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;
}

Permissions and Security

Permission Model

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

Security Considerations

  1. Key generation: Use ftok() with secure paths to avoid key collisions
  2. Permission setting: Set restrictive permissions (e.g., 0600) for sensitive data
  3. Cleanup: Always remove queues when done to prevent resource leaks
  4. Validation: Validate message content and size before processing
  5. Access control: Check permissions before queue operations

Best Practices

// 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;
}

Concurrency and Limitations

Concurrency Behavior

  • 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)

System Limitations

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

Performance Considerations

  1. Message size: Smaller messages are more efficient
  2. Queue depth: Deep queues can impact performance
  3. Memory usage: Messages consume kernel memory
  4. Context switching: Blocking operations cause context switches

Resource Management

// 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 Codes and Troubleshooting

Common Error Codes

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

Debugging Techniques

1. Check System Resources

# 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>

2. Error Handling Template

#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;
}

3. Diagnostic Functions

// 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
}

Troubleshooting Checklist

  1. 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
  2. Permission Problems:

    • Check queue permissions with ipcs -q
    • Verify process runs with appropriate user/group
    • Ensure read/write permissions match operations
  3. 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
  4. Resource Leaks:

    • Always clean up queues with msgctl(IPC_RMID)
    • Monitor system resources with ipcs
    • Implement proper error handling and cleanup
  5. Performance Issues:

    • Monitor queue depth and message sizes
    • Consider using multiple queues for high throughput
    • Implement timeouts for blocking operations
    • Profile application for bottlenecks

Example Cleanup Script

#!/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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment