Linux interprocess communication – message queue

1. The principle of message queue

Message queues provide a way to send a block of data from one process to another. Each data block is considered to contain a type, and the receiving process can independently receive databases containing values of different types.


A message is actually a data block, and this data block is a structure, which is named by itself. The first member of the message is fixed, it is a long integer, it represents the type of the message, and the following members are the data defined by itself. At the beginning, the message queue is empty, and a process a can send a message to the message queue, but if another process b or c receives the message in the message queue, it will be blocked. When a process sends a message to the message queue, it can be added as long as the message queue is not full. If the message queue is full, it will be blocked. When a process receives a message in the message queue, the type of the message can be specified. When there is no message of the specified type in the message queue, the process will be blocked.

1. Message queue and famous pipe

Message queues have many similarities to well-known pipes

Same point:

The use of message queues does not solve some of the problems we encounter when using well-known pipes, such as the blocking problem when the pipe is full, which also exists in the message queue, and blocking occurs when the message queue is full.

difference:

Compared with pipes, message queues have less complexity in opening and closing pipes. Message queues provide a fairly simple and efficient way of passing data between two unrelated processes. In contrast to named pipes, message queues exist independently of the sending and receiving processes, which eliminates some of the difficulties that can arise when synchronizing the opening and closing of named pipes.

2. Advantages and disadvantages of message queue:

(1) Advantages:

① The synchronization and blocking problems of well-known pipes can be almost completely avoided by sending messages.

② Some methods can be used to view emergency messages in advance.

shortcoming:

Like a pipeline, each data block has a maximum length limit, and the total length of all data blocks contained in all queues in the system also has an upper limit. The Linux system has two macro definitions MSGMAX and MSGMNB, which respectively define the maximum length of a message and the maximum length of a queue in bytes. These macro definitions may be different or not exist on other systems.

3. The life cycle of the message queue

The life cycle of the message queue does not end with the end of the process, but continues with the kernel. Call the kernel interface related to the message queue. The kernel helps to create it. As long as you don’t take the initiative to delete it, it will always exist. Even if the process has ended, they will always be maintained in the kernel. You can continue to use it when you want to use it, and delete it when the program is not used at the end. There are 3 ways to delete, shutting down, calling related functions to delete, and manually deleting on the command line.

The same is true for the life cycle of semaphores and shared memory.

2. Message queue related interface functions

1.msgget() creates a message queue

Create or get the ID of a message queue

 int msgget(key_t key, int msqflg);

Parameter explanation:
key: “room password”
msqflg: the way to create a message queue and set permissions at the same time
Return value: return message queue id if successful, return -1 if failed

Permission setting rules:

IPC_CREAT: can be used alone, if the message queue does not exist, it will be re-opened, the function return value is the ID of the newly opened message queue; if it already exists, the existing message queue will be used, and the function return value Is the ID of an existing message queue.

IPC_EXCL: It cannot be used alone, it must be used in conjunction with IPC_CREAT, that is, IPC_CREAT | IPC_EXCL, which means that if the message queue does not exist, it will be re-opened, and the return value of the function is the newly opened message queue ID; if it already exists, an error will be reported.

IPC_CREAT | IPC_EXCL | 0664: When creating a message queue, set the access rights of the message queue

2.msgsnd() Add a message to the message queue

The function of this function is to add a message to the message queue.

int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);

Parameter explanation:

msgid: message queue ID, the return value of the msgget function
msgp: The address of the message buffer, that is, the message you want to add to the message queue, needs to meet a certain format. The message format is as follows:

 struct msgbuf
{<!-- -->
long mtype; // Message type, must be greater than 0
char mtext[1]; // message data
//(The last member of the structure is an array, which is also called a flexible array, that is, the array size is variable)
};

msgsz: Specify the length of valid data in mtext. The message length here refers to the number of bytes occupied by the buf member in the above structure.
msgflag: Indicates the method of sending messages. Generally set to 0. IPC_NOWAIT can also be set. The optional values and their meanings are as follows:


Return value: Returns 0 on success, -1 on failure

3.msgrcv()) receives a message

The function of this function is to retrieve a message from the head of the message queue.

ssize_t msgrcv(int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg);

Parameter explanation:

msgid: message queue id
msgp: Output parameter, indicating the address of the message buffer. That is, where you want to put the retrieved message, you also need to use the specified format. The data format is the same as the message format to be added by the msgsnd function, as follows:

 struct msgbuf
{<!-- -->
long mtype; // Message type, must be greater than 0
char mtext[1]; // message data
//(The last member of the structure is an array, which is also called a flexible array, that is, the array size is variable)
};

size: The size of the buffer used to store received message data.
msgtype: Select the message type you want to fetch (although it is called a message queue, it does not necessarily mean fetching the queue head element, or it can fetch the first message with the same message type). Mainly divided into the following three situations:

The specific explanation of msgtype < 0: Assume that there are four types of messages in the message queue: 1, 3, 4, and 5. If msgtype = -4, the absolute value is 4, then you need to take out all messages with a message type less than or equal to 4. message, therefore, the message type of the fetched message must be type 1, 3, or 4.

msgflag: Indicates the method of receiving messages. Generally set to 0, you can also set IPC_NOWAIT, etc. The optional values are as follows:


Return value: msgrcv() successfully returns the length of the message data received in mtext, otherwise it returns -1.

4.msgctl() is generally used to destroy the message queue

The function of this function is to control the message queue and is generally used to destroy the message queue.

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Parameter explanation:

msgid: message queue id
cmd: Specific operations performed on the message queue, such as copying, querying, destroying, etc. Optional values are as follows:

buf: Message queue buffer. For example, when cmd is IPC_STAT, the message queue related information will be copied to the buffer.
Return value: msgctl() returns 0 on success and -1 on failure.