Implementing a Message Queue in C++

Have you ever experienced two processes frantically competing to read and write data, resulting in either fragmented data or the program getting stuck in “synchronization wait”?It’s like a cafeteria where no one is in line, everyone is pushing at the window, and in the end, the food spills, and no one gets to eat well.

In C++, a message queue is designed to solve this “chaotic situation” — it acts as an “intelligent courier transfer station” between processes, where the sender (sending process) throws packages (messages) in order, and the receiver (receiving process) can either take them in order or precisely pick the packages they want, all without facing each other in a “scramble”. Asynchronous communication is delightful!

Today, we will delve into the underlying logic and usage details of message queues, along with code examples, ensuring that you can get started right after reading!

Part 1What is a message queue?

The official definition: “A first-in, first-out queue data structure, an internal linked list in the system kernel”.

In layman’s terms:

  • It is a “safe warehouse” maintained by the kernel, so processes do not need to manage data storage and sorting themselves; the kernel handles everything;

  • The sending process places packages (messages) at the “tail end” of the warehouse, while the receiving process takes them from the “head” — but here’s the key point — they can take them out of order! For example, they can take only “fresh packages” (specific types of messages), even if they are hidden in the middle of the queue;

  • It supports multiple processes sending and receiving simultaneously; once a package is taken, it automatically disappears from the warehouse and cannot be retrieved again;

  • Processes do not need to have a “blood relationship” (like parent-child processes); as long as they know the “warehouse address” (key), they can communicate across processes.

For example: You (process A) want to send data to a colleague (process B); you do not need to wait for B to be ready; you can directly package the data into a “package” and throw it into the message queue. B can come to pick it up whenever they have time, and you can continue doing your work — this is completely asynchronous communication, which is much more advanced than the “must face-to-face synchronization” of pipes!

Part 2Structure

Since we are “sending packages”, there must be a unified package format; otherwise, the receiver cannot unpack it. The standard message structure in C++ looks like this (derived from UNIX system calls, applicable to Linux and BSD):

struct msgbuf {    long  mtype;   // Message type (must be a positive integer! The core of the core)    char  mtext[1];// Message data (can be of any type, this is a placeholder)};

These two members are indispensable, especially mtype — it is the “classification label” on the package, directly determining the flexibility of the message queue!

2.1. The Soul Member: mtype (Message Type)

A message queue without mtype is no different from a regular queue. With it, a message queue is equivalent to “multiple categorized shelves”:

  • The sender still throws packages at the tail of the queue but will attach a label (for example, 100, 200, 300);

  • The receiver can specify “only take packages with label 100”, even if packages with label 200 are in front, they can skip directly to take 100;

  • Once a message is taken, the positions of other labeled packages remain unchanged, and there will be no disorder.

For a practical scenario: a message queue simultaneously processes “user login messages” (mtype=10), “data query messages” (mtype=20), and “error log messages” (mtype=30). The receiving process A only processes login messages, while process B only processes error logs — there is no need to scramble for the same queue, nor manually filter; the kernel directly distributes by type, maximizing efficiency!

2.2. Flexible mtext: Message Data Can Be Stored in Any Way

mtext is the “payload (valid data)” of the message, and it is absolutely not limited to storing char arrays! You can change it to any type, even nested structures — as long as the formats of the sender and receiver are consistent, it can be parsed normally.

Here are 3 practical examples covering 90% of scenarios:

Example 1: Store an integer (like user ID)

struct msgbuf {    long  mtype;   // Label: for example, 10 (user ID message)    int   ntext;   // Data: directly store int type};

Example 2: Store a string + integer (like username + age)

struct msgbuf {    long  mtype;       // Label: 20 (user information message)    char  username[50];// Username (string)    int   age;         // Age (integer)};

Example 3: Nested structure (complex data, like order information)

If the data is too much, writing it directly in msgbuf would be too messy; nested structures are more standardized:

// Sub-structure: Order detailsstruct OrderInfo {    char  order_id[32];// Order number    float amount;      // Order amount    char  status[20];  // Order status (pending payment/completed)};// Message structure (package)struct msgbuf {    long        mtype;     // Label: 30 (order message)    struct OrderInfo info; // Nested order details};

💡 Reminder: No matter how you define it, mtype must be the first member, and its type must be long (not int! The length of long may vary across different systems, but the kernel will automatically handle compatibility).

Part 3Creating a Message Queue

To send packages, you first need a “courier transfer station”, which requires the msgget function (part of the sys/msg.h header file). Its purpose is to create a new message queue or access an existing one.

3.1. Function Prototype (No need to memorize, just understand the parameters)

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget(key_t key, int msgflg);

Return value: returnsmessage queue ID (msgid) (equivalent to the “internal number” of the transfer station), returns -1 on failure;

Two core parameters: key (address) and msgflg (creation/access rules).

3.2. Parameter Explanation: Don’t Get Confused by key and msgflg

(1) key: The “address” of the message queue (unique identifier)

To find the same message queue, processes must use the same key — just like you and your colleague need to know the same address to go to the same courier station. There are two common ways to set the key:

  • Custom constant: for example, 0x1234, 0xABCD (must be positive), suitable for inter-process communication (for example, if processes A and B both use 0x1234, they can find the same queue);

  • IPC_PRIVATE: creates a “private queue”, with a key value of 0, accessible only to the current process and its child processes (suitable for parent-child process communication, invisible to others).

💡 Fun fact: The kernel requires “key + message queue” to be unique, but allows multiple queues to have a key of 0 (because the queue created by IPC_PRIVATE is private and will not conflict).

(2) msgflg: Creation rules + access permissions

This is a “composite parameter” consisting of two parts:Access permissions (lower 9 bits) + Creation method (higher bits) .

  • Access permissions: similar to file permissions, for example, 0666 (read + write), 0644 (owner read/write, others read only). 0666 is not a devil number; it means “any process can read and write this queue”, convenient for testing; in actual projects, permissions should be narrowed based on requirements (for example, 0600, only the current user can access).

Creation methods (two commonly used):

  • IPC_CREAT: “open if exists, create if not” (most commonly used! For example, the first call creates the queue, and subsequent calls directly get the queue ID);

  • IPC_EXCL: must be used with IPC_CREAT, meaning “if the queue already exists, report an error” (to prevent duplicate creation, ensuring that each run of the program is a new queue).

3.3. Practical Example: Creating a Message Queue

Example 1: Create/open the queue with key=0x1234 (common scenario)

int msgid;// 0666: access permissions, IPC_CREAT: open if exists, create if notmsgid = msgget(0x1234, 0666 | IPC_CREAT);if (msgid == -1) {    perror("Failed to create queue"); // Print error reason (e.g., insufficient permissions, key already occupied)    exit(1);}

Example 2: Create a brand new queue (error if it already exists)

int msgid = msgget(0x1234, 0666 | IPC_CREAT | IPC_EXCL);if (msgid == -1) {    perror("Queue already exists, creation failed"); // Suitable for initialization scenarios, to avoid reusing old queues    exit(1);}

Part 4Sending and Receiving Messages in the Message Queue

Once the queue is created, it’s time to “send packages” (msgsnd) and “retrieve packages” (msgrcv) — these two functions are the core of the message queue, similar to read/write but with added control over message types.

4.1. Sending Messages: msgsnd Function — Throwing Packages into the Transfer Station

(1) Function Prototype

int msgsnd(int msqid, void *msgp, int msgsz, int msgflg);
  • Return value: returns 0 on success, -1 on failure;

Parameter explanation:

  • msqid: message queue ID (the one returned by msgget);

  • msgp: pointer to the message structure (which is our defined msgbuf, containing mtype and mtext);

  • msgsz: length of the message data (note: only counts the length of mtext, not mtype! And must be greater than 0);

  • msgflg: sending method (0 = blocking, IPC_NOWAIT = non-blocking).

(2) Key Point: Don’t Miscalculate msgsz!

For example, our defined msgbuf is:

struct msgbuf {    long mtype;    char ctext[100];};

When sending, msgsz should be strlen(buf.ctext) (since ctext stores a string, the length is the actual character count), not sizeof(buf.ctext) (100 bytes, which would send unnecessary empty characters, wasting space).

For example, in the case of nested structures:

struct msgbuf {    long mtype;    OrderInfo info; // Assume OrderInfo size is 64 bytes};

msgsz would be sizeof(OrderInfo) (64 bytes) — in short, msgsz = actual size of the message data (mtext part).

(3) Blocking vs Non-blocking: Will Sending Messages Block?

When msgflg=0, it is blocking mode (default); if the message queue is full, the process will wait until there is space in the queue to send; when msgflg=IPC_NOWAIT, it is non-blocking mode, and if the queue is full, it directly returns -1, setting errno to EAGAIN (which can be understood as “not available now, try again later”).

When will the queue be full?

  • The total bytes used in the queue (msg_cbytes) + the length of the new message (msgsz) > the maximum capacity of the queue (msg_qbytes);

  • The total number of messages in all message queues in the system reaches the kernel limit (which is generally rare).

(4) Five Steps to Send a Message (Practical Process)

Taking “sending a string message” as an example, every step is crucial:

#include <sys/msg.h>#include <string.h>#include <errno.h>// 1. Define message structure (must be consistent with the receiver!)struct msgbuf {    long mtype;    char ctext[100];};#define KEY 0x1234   // Address#define TYPE 100     // Message type labelint main() {    // 2. Open/create message queue    int msgid = msgget(KEY, 0666 | IPC_CREAT);    if (msgid == -1) { perror("msgget failed"); return 1; }    // 3. Assemble message (fill label + insert data)    struct msgbuf buf;    buf.mtype = TYPE; // Label must be a positive integer!    strcpy(buf.ctext, "Hello, message queue!"); // Data to be sent    // 4. Send message (msgsz is the actual length of ctext)    int ret;    while (1) {        ret = msgsnd(msgid, &buf, strlen(buf.ctext), 0); // Blocking mode        if (ret == 0) {            printf("Message sent successfully!\n");            break;        } else if (errno == EINTR) {            // Interrupted by signal (like Ctrl+C), just retry sending            printf("Sending interrupted, retrying...\n");            continue;        } else {            perror("Sending failed");            break;        }    }    return 0;}

(5) Advanced Practice: Loop to Read Keyboard Input and Send to Queue

For example, create a “message sender” that sends whatever is input to the queue, and exits on input “exit”:

#include <sys/msg.h>#include <stdio.h>#include <string.h>#include <errno.h>struct mymsgbuf {    long mtype;    char ctext[100];};#define KEY 0x1234int main() {    struct mymsgbuf buf;    int msgid = msgget(KEY, 0666 | IPC_CREAT);    if (msgid == -1) { perror("msgget failed"); return 1; }    // Loop to read keyboard input until "exit" is entered    while (1) {        memset(&buf, 0, sizeof(buf)); // Clear buffer to avoid dirty data        printf("Please enter the message to send (input exit to quit):");        fgets(buf.ctext, sizeof(buf.ctext), stdin); // Read input        // Input exit, exit loop        if (strncmp(buf.ctext, "exit", 4) == 0) {            printf("Exiting sender...\n");            break;        }        // Set message type to current process ID (to help the receiver know who sent it)        buf.mtype = getpid();        // Send message, retry if interrupted        while (msgsnd(msgid, &buf, strlen(buf.ctext), 0) == -1) {            if (errno != EINTR) {                perror("Sending failed");                return 1;            }        }    }    return 0;}

4.2. Receiving Messages: msgrcv Function — Precisely Retrieve the Package You Want

Receiving messages is a bit more complex than sending; the core is the msgtyp parameter (specifying which type of message to retrieve).

(1) Function Prototype

int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg);

Return value: returns the length of the received message data (length of mtext) on success, -1 on failure;

Parameter explanation (focus on msgtyp and msgflg):

  • msqid/msgp/msgsz: consistent with msgsnd (msgsz is the maximum capacity of the buffer, exceeding it will truncate or report an error);

  • msgtyp: specifies the type of message to receive (core!):

      • 0: “do not pick, take the first message in order” (normal queue mode);

      • Positive integer: “only take the first message of type equal to msgtyp” (precise filtering, for example, only take label 100);

      • Negative integer: “take the first message of type less than or equal to |msgtyp|” (range filtering, for example, msgtyp=-200, take type ≤200);

  • msgflg: receiving method:

      • 0: blocking mode (wait if the queue is empty until there is a message);

      • IPC_NOWAIT: non-blocking mode (returns -1 if the queue is empty, errno=ENOMSG);

      • MSG_NOERROR: if the message data length exceeds msgsz, it will be truncated directly (no error reported); by default, it reports an error (E2BIG).

(2) Five Steps to Receive a Message (Practical Process)

Corresponding to sending, the receiver’s message structure must be consistent with the sender’s:

#include <sys/msg.h>#include <string.h>#include <errno.h>// Must be exactly the same as the sender's structure!struct msgbuf {    long mtype;    char ctext[100];};#define KEY 0x1234#define TYPE 100int main() {    // 1. Open message queue (use the same KEY as the sender)    int msgid = msgget(KEY, 0666 | IPC_CREAT);    if (msgid == -1) { perror("Failed to open queue"); return 1; }    struct msgbuf buf;    int ret;    while (1) {        // 2. Clear buffer        memset(&buf, 0, sizeof(buf));        // 3. Receive message: only take messages of type=TYPE, blocking mode        ret = msgrcv(msgid, &buf, sizeof(buf.ctext), TYPE, 0);        if (ret == -1) {            if (errno == EINTR) {                printf("Receiving interrupted, retrying...\n");                continue;            }            perror("Receiving failed");            break;        }        // 4. Process message (just print here)        printf("Received message: type=%ld, length=%d, content: %s\n", buf.mtype, ret, buf.ctext);        // 5. If received exit, exit        if (strncmp(buf.ctext, "exit", 4) == 0) {            printf("Received exit command, exiting receiver...\n");            break;        }    }    return 0;}

(3) Advanced Practice: Receive Any Type of Message and Print Details

For example, create a “message receiver” that receives all messages sent to the queue with 0x1234, printing type, length, and content:

#include <sys/msg.h>#include <stdio.h>#include <string.h>#include <errno.h>struct mymsgbuf {    long mtype;    char ctext[100];};#define KEY 0x1234int main() {    struct mymsgbuf buf;    int msgid = msgget(KEY, 0666 | IPC_CREAT);    if (msgid == -1) { perror("msgget failed"); return 1; }    printf("Receiver started, waiting for messages... (input exit to quit)\n");    while (1) {        memset(&buf, 0, sizeof(buf));        // msgtyp=0: receive the first message in the queue (no type filtering)        int ret = msgrcv(msgid, &buf, sizeof(buf.ctext), 0, 0);        if (ret == -1) {            if (errno == EINTR) continue;            perror("msgrcv failed");            return 1;        }        // Print message details        printf("【Message Details】Type: %ld | Length: %d bytes | Content: %s",                buf.mtype, ret, buf.ctext);        // If received exit, exit        if (strncmp(buf.ctext, "exit", 4) == 0) {            printf("Exiting receiver...\n");            break;        }    }    return 0;}

4.3. Testing: Let the Sender and Receiver Run

  1. Compile the sender: g++ sender.cpp -o sender;

  2. Compile the receiver: g++ receiver.cpp -o receiver;

  3. First, start the receiver: ./receiver (blocking wait for messages);

  4. Then start the sender: ./sender, input a message (like “Hello message queue!”), the receiver will print it immediately;

  5. Input “exit”, both the sender and receiver will exit.

Try this process, and you will find that even if the sender sends a message while the receiver is not started, the receiver can still receive it after it starts — this is the charm of asynchronous communication!

Part 5Examples

  • Both pieces of code are based on Linux/UNIX systems (relying on system headers like sys/msg.h, Windows needs adaptation);

  • Consistently use KEY=0x1234 as the message queue identifier to ensure both ends can associate with the same queue;

  • The message structure includes **process ID (mtype)** and **string data (ctext)**, making it easy to identify the sender;

  • The receiving end automatically deletes the queue upon exit to avoid kernel residue (solving the previously mentioned “queue not deleted” issue).

Sender Code (sender.cpp)

Function: Continuously read keyboard input and send messages to the queue, input “exit” to quit.

#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>#include <sys/msg.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>// 1. Unified message structure (must be exactly the same as the receiver!)struct MsgBuffer {    long mtype;          // Message type: identified by the sending process ID (ensure positive integer)    char ctext[256];     // Message data: stores the string input from the keyboard}; // 2. Define queue key (must be the same on both ends)#define MSG_QUEUE_KEY 0x1234// 3. Maximum length of message data (only counts ctext, not mtype)#define MAX_MSG_DATA_LEN sizeof(((struct MsgBuffer*)0)->ctext)int main() {    // 4. 1. Create/open message queue    int msg_queue_id = msgget(MSG_QUEUE_KEY, 0666 | IPC_CREAT);    if (msg_queue_id == -1) {  // Error handling: creation failed        perror("【Sender】msgget failed to create queue");  // Print specific error reason (like permissions, key conflict)        exit(EXIT_FAILURE);    }    std::cout << "【Sender】Message queue created successfully, queue ID:" << msg_queue_id << std::endl;    MsgBuffer msg;    std::string input;    // 5. Loop to send messages (until input "exit")    while (true) {        // 5.1 Read keyboard input        std::cout << "【Sender】Please enter message content (input exit to quit):";        std::getline(std::cin, input);  // Supports input with spaces (more user-friendly than fgets)        // 5.2 Check for exit command        if (input == "exit") {            // Send "exit" message to the receiver, notifying it to exit            msg.mtype = getpid();  // Set mtype to current process ID (the receiver can identify the sender)            strncpy(msg.ctext, input.c_str(), MAX_MSG_DATA_LEN - 1);  // Avoid buffer overflow            msg.ctext[MAX_MSG_DATA_LEN - 1] = '\0';  // Ensure string termination            // Send exit message (non-blocking mode, to prevent blocking)            if (msgsnd(msg_queue_id, &msg, strlen(msg.ctext), IPC_NOWAIT) == -1) {                perror("【Sender】Failed to send exit message");            } else {                std::cout << "【Sender】Exit command sent, exiting..." << std::endl;            }            break;        }        // 5.3 Assemble message (key: mtype must be a positive integer)        msg.mtype = getpid();  // Use process ID as message type, the receiver can distinguish different senders        strncpy(msg.ctext, input.c_str(), MAX_MSG_DATA_LEN - 1);  // Safe copy (to prevent overflow)        msg.ctext[MAX_MSG_DATA_LEN - 1] = '\0';  // Manually add string terminator        // 5.4 Send message (blocking mode, wait if the queue is full; retry on signal interruption)        int send_ret;        while (true) {            send_ret = msgsnd(                msg_queue_id,    // Queue ID                &msg,            // Message buffer address                strlen(msg.ctext),// Message data length (only counts ctext! not including mtype)                0                // Blocking mode (0), wait if the queue is full            );            if (send_ret == 0) {                std::cout << "【Sender】Message sent successfully! Content:" << msg.ctext << std::endl;                break;  // Sending successful, exit retry loop            } else if (errno == EINTR) {                // Interrupted by signal (like Ctrl+C), retry sending                std::cout << "【Sender】Sending interrupted, retrying..." << std::endl;                continue;            } else {                // Other errors (like queue not existing, insufficient permissions)                perror("【Sender】Message sending failed");                exit(EXIT_FAILURE);            }        }    }    return 0;}

Receiver Code (receiver.cpp)

Function: Continuously receive messages from the queue, print sender ID and message content, delete the queue and exit upon receiving “exit”.

#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>#include <sys/msg.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>// 1. Unified message structure (must be exactly the same as the sender!)struct MsgBuffer {    long mtype;          // Message type: corresponds to the sender's process ID    char ctext[256];     // Message data: stores the received string}; // 2. Queue key (same as sender)#define MSG_QUEUE_KEY 0x1234// 3. Maximum length of message data (same as sender)#define MAX_MSG_DATA_LEN sizeof(((struct MsgBuffer*)0)->ctext)// 4. Cleanup function: delete message queue upon program exit (to avoid kernel residue)void cleanMsgQueue(int msg_queue_id) {    if (msgctl(msg_queue_id, IPC_RMID, NULL) == 0) {        std::cout << "【Receiver】Message queue successfully deleted (ID:" << msg_queue_id << ")" << std::endl;    } else {        perror("【Receiver】Failed to delete message queue");    }}int main() {    // 5. 1. Create/open message queue (same key as sender)    int msg_queue_id = msgget(MSG_QUEUE_KEY, 0666 | IPC_CREAT);    if (msg_queue_id == -1) {        perror("【Receiver】Failed to open message queue");        exit(EXIT_FAILURE);    }    std::cout << "【Receiver】Message queue opened successfully, queue ID:" << msg_queue_id << std::endl;    std::cout << "【Receiver】Waiting for messages... (input exit to terminate)" << std::endl;    // Register cleanup function: automatically delete queue upon program exit (catch normal and signal exits)    atexit([&]() { cleanMsgQueue(msg_queue_id); });    MsgBuffer msg;    ssize_t recv_len;  // Length of received message data (msgrcv return value)    // 6. Loop to receive messages    while (true) {        // 6.1 Clear buffer (to avoid residual old data)        memset(&msg, 0, sizeof(MsgBuffer));        // 6.2 Receive message (key: msgtyp=0 means receive all types of messages)        recv_len = msgrcv(            msg_queue_id,    // Queue ID            &msg,            // Receive buffer address            MAX_MSG_DATA_LEN,// Maximum capacity of buffer (not including mtype)            0,               // msgtyp=0: receive the first message in the queue (no type filtering)            0                // Blocking mode: wait if the queue is empty        );        // 6.3 Process receive result        if (recv_len == -1) {            if (errno == EINTR) {                // Interrupted by signal, retry receiving                std::cout << "【Receiver】Receiving interrupted, retrying..." << std::endl;                continue;            } else {                perror("【Receiver】Message receiving failed");                exit(EXIT_FAILURE);            }        }        // 6.4 Parse message (check if it is an exit command)        std::cout << "\n【Receiver】Received message:" << std::endl;        std::cout << "  Sender Process ID:" << msg.mtype << std::endl;  // mtype=sender PID        std::cout << "  Message Length:" << recv_len << " bytes" << std::endl;        std::cout << "  Message Content:" << msg.ctext << std::endl;        // 6.5 Check for exit command (terminate on receiving "exit")        if (strcmp(msg.ctext, "exit") == 0) {            std::cout << "\n【Receiver】Received exit command, terminating..." << std::endl;            break;        }    }    return 0;}

Part 6Precautions

  • Messages are deleted once read: Once the receiver takes the message, it is gone from the queue, so if multiple processes need to receive the same message, they must use a “copy message” approach (for example, the receiver takes it and then forwards it to other processes);

  • Do not include mtype in msgsz: Many beginners mistakenly set msgsz to sizeof(buf) (including the 4 bytes of mtype), causing the receiver to parse incorrectly — remember: msgsz only counts the length of mtext;

  • Permission issues: If you create the queue with 0600, other users’ processes will not be able to open the queue (msgid=-1, errno=EACCES); use 0666 for testing, and narrow permissions in actual projects;

  • The queue will block when full: If the sender keeps sending and the receiver does not receive, the queue will fill up, and at this point, msgsnd will block (by default) until there is space — you can use IPC_NOWAIT non-blocking mode to avoid the process getting stuck;

  • Message types must be positive integers: If mtype is set to 0 or negative, msgsnd will return -1 (errno=EINVAL);

  • Don’t forget to delete the queue: Queues created by msgget will persist in the kernel (even if the program exits), and the next run will reuse the old queue. To delete the queue, use msgctl(msgid, IPC_RMID, NULL) (for example, call it when the program exits).

Summary

Compared to pipes and shared memory, the core advantages of message queues are “flexibility + asynchrony + safety”:

  1. No blood relationship required: Any process can communicate as long as it knows the key (pipes mostly require parent-child processes);

  2. Categorized reception: Messages can be filtered by mtype, eliminating the need for manual data classification;

  3. Asynchronous communication: The sender does not need to wait for the receiver, and the receiver does not need to wait for the sender;

  4. Data safety: The kernel maintains the queue, automatically handling synchronization, preventing data competition (shared memory requires manual locking, pipes require synchronized read/write);

  5. Message packaging: Messages are transmitted as units, preventing “data sticking” like in pipes (for example, if the sender sends two messages, the receiver will not read them all at once).

Of course, it also has drawbacks: for example, messages have a length limit (the system default is generally 8192 bytes, which can be modified via msgctl), and it is slower than shared memory (because data must be copied to the kernel) — but for most medium to low concurrency scenarios, the flexibility of message queues can fully compensate for these shortcomings!

Previous Recommendations

[Standard of Big Companies] Linux C/C++ Backend Advanced Learning Path

Linux Kernel Learning Guide, Hardcore Training Manual

C++ Qt Learning Path from A to Z! (Desktop Development & Embedded Development)

Click below to follow 【Linux Tutorials】 to get programming learning paths, project tutorials, resume templates, big company interview questions in PDF format, big company interview experiences, programming communication circles, etc., more practical, understandable, and reproducible technical articles are available at 【Linux Tutorials】.

Leave a Comment