Chapter 46 · File 3 of 6
Sending & Receiving Messages
msgsnd() · msgrcv() · Message Structure · Flags · Blocking
Once you have a queue ID, you use msgsnd() to put messages in and msgrcv() to take them out. These two calls form the core of all message queue I/O. This file explains both in detail with examples.
The Message Structure
Before calling msgsnd or msgrcv, you must define your message structure. The rule is simple: the first field must be a long called mtype. Everything after that is your payload — you design it however you want.
/* The general message template */
struct mymsg {
long mtype; /* REQUIRED: message type. Must be > 0 */
char mtext[]; /* Payload: can be any data, any size */
};
/* Real examples — you design the payload */
/* Example 1: Simple text message */
struct TextMsg {
long mtype;
char text[256];
};
/* Example 2: Numeric command message */
struct CmdMsg {
long mtype;
int command;
int value;
};
/* Example 3: Sensor data */
struct SensorMsg {
long mtype; /* 1=temperature, 2=pressure, 3=humidity */
int sensor_id;
float reading;
long timestamp;
};
/* Example 4: Zero-length payload — the type IS the message */
struct SignalMsg {
long mtype; /* e.g. 1=READY, 2=DONE, 3=ERROR */
/* no payload needed — the type encodes the event */
};
Section 46.2.1 — msgsnd(): Sending Messages
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/* msqid — queue identifier from msgget() msgp — pointer to your message struct msgsz — size of the PAYLOAD only (NOT the full struct) = sizeof(struct YourMsg) – sizeof(long) msgflg — 0 for blocking, IPC_NOWAIT for non-blocking Returns 0 on success, -1 on error */
The msgsz Argument — Common Mistake!
The size argument is the size of the payload only — not the whole struct. You must subtract the size of mtype (which is a long = 8 bytes on 64-bit, 4 bytes on 32-bit).
struct MyMsg {
long mtype; /* 8 bytes on 64-bit */
char text[100]; /* 100 bytes */
};
struct MyMsg m;
/* WRONG — includes mtype in size */
msgsnd(qid, &m, sizeof(struct MyMsg), 0); /* sends 108 bytes — wrong! */
/* CORRECT — payload size only */
msgsnd(qid, &m, sizeof(m.text), 0); /* sends 100 bytes — correct */
/* Also correct using offsetof trick */
msgsnd(qid, &m, sizeof(struct MyMsg) - sizeof(long), 0);
Blocking Behaviour of msgsnd()
msgsnd() blocks (waits) if the queue is full. The queue has a maximum byte limit (default 16384 bytes on most systems). If adding the message would exceed this, msgsnd blocks until space becomes available.
| Situation | Behaviour with msgflg=0 | Behaviour with IPC_NOWAIT |
|---|---|---|
| Queue has space | Returns 0 immediately ✓ | Returns 0 immediately ✓ |
| Queue is full | Blocks until space available | Returns -1, errno = EAGAIN |
| Queue is deleted while blocked | Returns -1, errno = EIDRM | N/A (not blocking) |
| Signal caught while blocked | Returns -1, errno = EINTR | N/A (not blocking) |
Section 46.2.2 — msgrcv(): Receiving Messages
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t maxmsgsz, long msgtyp, int msgflg);
/* msqid — queue identifier msgp — buffer to receive the message into maxmsgsz — max payload bytes your buffer can hold msgtyp — which message type to receive (see table below) msgflg — IPC_NOWAIT, MSG_NOERROR, MSG_EXCEPT Returns: number of bytes in payload on success, -1 on error */
The msgtyp Argument — Type Selection Rules
| msgtyp value | Which message is returned |
|---|---|
0 |
First message in the queue (FIFO, any type) |
> 0 (e.g. 3) |
First message with mtype == 3 |
< 0 (e.g. -5) |
First message with mtype ≤ abs(-5) = 5 — lowest type first |
msgrcv() Flags
| Flag | Effect |
|---|---|
IPC_NOWAIT |
Don’t block if no matching message — return -1 with errno=ENOMSG |
MSG_NOERROR |
If message payload > maxmsgsz, silently truncate instead of failing |
MSG_EXCEPT |
Receive first message whose type is NOT equal to msgtyp (Linux-specific) |
Code Example 1 — Basic Send and Receive
/* basic_send_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 128
struct Message {
long mtype;
char mtext[MAX_TEXT];
};
int main(void)
{
int qid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
if (qid == -1) { perror("msgget"); return 1; }
/* --- SENDING --- */
struct Message out;
out.mtype = 42; /* type = 42 */
strncpy(out.mtext, "System V rocks!", MAX_TEXT - 1);
/* msgsz = payload size only = sizeof(mtext) */
if (msgsnd(qid, &out, sizeof(out.mtext), 0) == -1) {
perror("msgsnd");
return 1;
}
printf("Sent: type=%ld, text='%s'\n", out.mtype, out.mtext);
/* --- RECEIVING --- */
struct Message in;
/* msgrcv returns number of bytes copied into mtext */
ssize_t n = msgrcv(qid, &in, sizeof(in.mtext),
42, /* receive only type=42 */
0); /* blocking */
if (n == -1) {
perror("msgrcv");
return 1;
}
printf("Received: type=%ld, text='%s', bytes=%zd\n",
in.mtype, in.mtext, n);
msgctl(qid, IPC_RMID, NULL);
return 0;
}
Code Example 2 — Non-Blocking Receive with EAGAIN Handling
/* nonblock_recv.c — Poll queue without blocking */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct Msg { long mtype; char text[64]; };
int main(void)
{
int qid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
/* Send one message */
struct Msg out = { .mtype = 1 };
strncpy(out.text, "Non-blocking demo", sizeof(out.text)-1);
msgsnd(qid, &out, sizeof(out.text), 0);
struct Msg in;
int count = 0;
while (1) {
/* IPC_NOWAIT: don't block — return immediately if empty */
ssize_t n = msgrcv(qid, &in, sizeof(in.text), 0, IPC_NOWAIT);
if (n == -1) {
if (errno == ENOMSG) {
/* Queue is empty — no message of requested type */
printf("Queue empty, poll %d. Sleeping 1 second...\n", ++count);
if (count >= 3) break; /* stop after 3 polls */
sleep(1);
continue;
}
perror("msgrcv");
break;
}
printf("Got message: type=%ld, text='%s'\n", in.mtype, in.text);
}
msgctl(qid, IPC_RMID, NULL);
return 0;
}
Code Example 3 — Priority Messaging with Negative msgtyp
/* priority_msgs.c — Using negative msgtyp for priority */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct Msg { long mtype; char text[64]; };
int main(void)
{
int qid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
/* Send messages with priorities: lower number = higher priority */
struct Msg m;
m.mtype = 3; strcpy(m.text, "Priority 3 (LOW)");
msgsnd(qid, &m, sizeof(m.text), 0);
m.mtype = 1; strcpy(m.text, "Priority 1 (CRITICAL)");
msgsnd(qid, &m, sizeof(m.text), 0);
m.mtype = 2; strcpy(m.text, "Priority 2 (HIGH)");
msgsnd(qid, &m, sizeof(m.text), 0);
printf("Sent: P3, P1, P2 in that order\n");
printf("Reading with msgtyp = -10 (lowest mtype first):\n\n");
/* Receive 3 messages — each time get the one with lowest mtype */
for (int i = 0; i < 3; i++) {
struct Msg rcv;
/* msgtyp = -10: get first message where mtype <= 10, lowest first */
msgrcv(qid, &rcv, sizeof(rcv.text), -10, 0);
printf(" Got type=%ld: '%s'\n", rcv.mtype, rcv.text);
}
msgctl(qid, IPC_RMID, NULL);
return 0;
}
/* Output:
Got type=1: 'Priority 1 (CRITICAL)'
Got type=2: 'Priority 2 (HIGH)'
Got type=3: 'Priority 3 (LOW)'
*/
Code Example 4 — MSG_NOERROR for Oversized Messages
/* msg_noerror.c — Truncation with MSG_NOERROR */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
/* Sender uses a large buffer */
struct BigMsg { long mtype; char text[128]; };
/* Receiver has a small buffer */
struct SmallBuf { long mtype; char text[32]; };
int main(void)
{
int qid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
/* Send 128 bytes of payload */
struct BigMsg big;
big.mtype = 1;
memset(big.text, 'X', sizeof(big.text) - 1);
big.text[127] = '\0';
msgsnd(qid, &big, sizeof(big.text), 0);
printf("Sent %zu bytes\n", sizeof(big.text));
/* Try to receive into small buffer WITHOUT MSG_NOERROR */
struct SmallBuf small;
ssize_t n = msgrcv(qid, &small, sizeof(small.text), 1, 0);
if (n == -1)
printf("Without MSG_NOERROR: failed (E2BIG) — message too large\n");
/* Send again since the message was not consumed on error */
msgsnd(qid, &big, sizeof(big.text), 0);
/* Receive WITH MSG_NOERROR — truncates silently */
n = msgrcv(qid, &small, sizeof(small.text), 1, MSG_NOERROR);
if (n != -1)
printf("With MSG_NOERROR: received %zd bytes (truncated)\n", n);
msgctl(qid, IPC_RMID, NULL);
return 0;
}
Handling Interrupted Calls (EINTR)
If a signal arrives while msgsnd/msgrcv is blocking, the call returns -1 with errno=EINTR. Always check for this and retry:
/* Helper: msgsnd that auto-retries on EINTR */
int msgsnd_safe(int qid, void *msgp, size_t sz, int flags)
{
int ret;
do {
ret = msgsnd(qid, msgp, sz, flags);
} while (ret == -1 && errno == EINTR);
return ret;
}
/* Helper: msgrcv that auto-retries on EINTR */
ssize_t msgrcv_safe(int qid, void *msgp, size_t sz, long type, int flags)
{
ssize_t ret;
do {
ret = msgrcv(qid, msgp, sz, type, flags);
} while (ret == -1 && errno == EINTR);
return ret;
}
