Inter-process communication - ------------------------- message queue

Posted by haku87 on Sun, 28 Jul 2019 08:03:41 +0200

XSI IPC 

There are three kinds of IPCs we call XSI IPC, namely message queues, semaphores, and shared memory. There are many similarities between them.

Specific can be seen The same characteristics of XSI IPC

This blog focuses on their differences:

Message queue

Message queues are linked lists of messages stored in the kernel and identified by message queue identifiers. As shown in the figure:

Message queues have many similarities with named pipes, but less complexity in opening and closing pipes. But using message queues does not solve the problems we encounter when using named pipes, such as blocking when the pipes are full.

Message queuing provides a fairly simple and effective way to transfer data between two unrelated processes.

Compared with named pipes, message queues exist independently of sending and receiving processes, which eliminates some of the difficulties that may arise when synchronizing the opening and closing of named pipes.

Advantage:

We can almost completely avoid the synchronization and blocking of named pipes by sending messages.

We can use some methods to check the emergency message in advance.

Disadvantages:

Like pipelines, each data block has a maximum length limit, and the total length of all data blocks contained in all queues in the system has an upper limit.

Message queue creation:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key,int flag);  

/*
key: The key value associated with the message queue; IPC_PRIVATE can be defined with macros, or ftok() can be used.
flag: Access privileges for message queues
 Return value: Return message queue ID if successful and - 1 if error occurs
*/

msgget is used to create a new queue or open an existing queue.

Each message queue has an msqid_ds structure associated with it:

struct msqid_ds {
    struct ipc_perm msg_perm;     /* Ownership and permissions */
    time_t          msg_stime;    /* Time of last msgsnd(2) */
    time_t          msg_rtime;    /* Time of last msgrcv(2) */
    time_t          msg_ctime;    /* Time of last change */
    unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */
    msgqnum_t       msg_qnum;     /* Current number of messages in queue */
    msglen_t        msg_qbytes;   /* Maximum number of bytes allowed in queue */
    pid_t           msg_lspid;    /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};

This structure specifies the current status of the queue. The members shown in the structure are defined by Single UNIX Specification.

When a new queue is created, the following members in the msqid_ds structure are initialized:

XSI IPC sets up an ipc_perm structure for each IPC structure. This structure specifies the authority and owner. It contains at least the following members:

 struct ipc_perm{     
              uid_t             uid;               /*Effective User ID of Shared Memory Owner */
              gid_t             gid;              /* Effective Group ID of the Group to which the Shared Memory Owner belongs*/
              uid_t             cuid;            /* Effective User ID of Shared Memory Creator*/
              gid_t             cgid;           /* Effective Group ID of the Group to which the Shared Memory Creator belongs*/
              unsigned short    mode;          /* Permissions + SHM_DEST SHM_LOCKED flag*/
              unsignedshort     seq;          /* serial number*/
.
.
.
};

Each implementation contains additional members in its ipc_perm structure. When an IPC structure is created, all fields are initialized. Thereafter, you can call msgctl, semctl, or shmctl to modify uid, gid, and mode fields. To change these values, the calling process must be the creator or superuser of the IPC structure.

The mode members in this structure are set according to the corresponding permission bits in flag, but there is no permission to execute for any IPC structure. The value of the flag field is specified in the following table:

msg_qnum, msg_lspid, msg_lrpid, msg_stime and msg_rtime are all set to 0.

msg_ctime is set to the current time.

msg_qbytes is set to the system limit.

If successful, msgget returns a non-negative queue ID. Thereafter, this value can be used for the other three message queue functions.

Operating message queue:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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

/*
msqid: Queue ID of message queue
cmd:   Describes the commands to be executed on queues specified by msqid:
            
          IPC_STAT     Take the msqid_ds structure of this queue and store it in the structure that buf points to.

          IPC_SET       Set the following four fields in the structure associated with this queue according to the value pointed by buf to the structure:

                     msg_perm.uid,msg_perm.gid,msg_perm.mode And msg_qbytes.
                     This command can only be executed by the following two processes:
                     One is that its effective user ID is equal to msg_perm.cuid or msg_perm.uid.
                     Another is a process with superuser privileges. Only superusers can increase the value of msg_qbyz.

         IPC_RMID    Delete the message queue from the system and all data that is still in the queue. This deletion takes effect immediately.
                     Other processes that are still using this message queue when they next attempt to operate on the queue,
                      Errors will be returned to EIDRM. This command can only be executed by the following two processes:
                     One is that its effective user ID is equal to msg_perm.cuid or msg_perm.uid.
                     The other is a process with superuser privileges.

These three commands (IPC_STAT, IPC_SET and IPC_RMID) can also be used for semaphores and shared memory.

buf: Message queue buffer
 Return value: Return 0 if successful and - 1 if error occurs
*/

             

Send a message:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid,const void* ptr,size_t nbytes,int flag); 

/*
msqid: ID of message queue
ptr: A pointer to a message. The commonly used message structure msgbuf is as follows:
struct msgbuf

{

    long mtype;          //Message type

    char mtext[N]       //Message body

}; 

nbytes: Number of bytes of message body sent

flag: 

           IPC_NOWAIT  The message returns immediately if it is not sent a completion function.

            0: Not until the completion function is sent back

Return value: Return 0 if successful and - 1 if error occurs
*/

Msgsnd adds new messages to the end of the message queue. Each message consists of three parts: a positive-length integer type field, a non-negative length, and actual data bytes (corresponding to length), all of which transmit messages to msgsnd when they are added to the message queue.

The ptr parameter points to a long integer that contains a positive integer message type followed by message data. (If nbytes is 0, there is no message data)

If the longest message sent is 512 bytes, the following structure can be defined:

struct mymesg{

  long mtype;
  char mtext[512];
};

Thus, ptr is a pointer to the mymesg structure. Receivers can cancel messages in a non-first-in, first-out order using the message type.

The parameter nbytes refers to the data length of mymseg.mtext.

The value of the parameter flag can be specified as IPC_NOWAIT. This is similar to the non-blocking I/O flag of file I/O. If the message queue is full (or the total number of messages in the message queue equals the system limit value, or the total number of bytes in the queue equals the system limit value), specify IPC_NOWAIT to cause msgsnd to return EAGAIN immediately in error. If IPC_NOWAIT is not specified, the process blockages until the following occurs:

(1) There is room for messages to be sent

(2) Delete the queue from the system and return to EIDRM("Identifier Deleted")

(3) A signal is captured and returned to EINTR from the signal processing program.

Note that the processing of deleting message queues is not perfect. Because there is no reference counter set for each message queue (this reference count is available for open files), deleting a queue causes the process that is still using the queue to return incorrectly the next time it operates on the queue. The semaphore mechanism also handles its deletion in the same way. Instead, when deleting a file, you wait until the last process of the file closes its file descriptor to delete the contents of the file.

When msgsnd returns successfully, the msqid_ds structure associated with the message queue is updated to identify the process ID (msg_lspid), the time to make the call (msg_stime), and an additional message (msg_qnum) is added to the queue.

receive messages

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>


int msgrcv(int msqid,  void* ptr,  size_t  nbytes,  long type,  int  flag);


/*
msqid: ID of message queue
ptr: Buffer to receive messages
nbytes: If the number of bytes of the message to be received is greater than nbytes and MSG_NOERROR is set in flag, the message is truncated (in this case, the truncated part of the message is discarded without informing us that the message is truncated). If this flag is not set and the message is too long, the error returns to E2BIG (the message is still in the queue)

type: Specifies which message type value you want to read in non-first-in, first-out order with a non-zero value
             0: Returns the first message in the message queue.
         More than 0: The first type of message in the return message queue is type.
         Less than 0: Returns messages whose type value is not greater than the absolute value of type in the message queue.
               If there are several such messages, the message with the smallest type value is chosen.


flag:  
          IPC_NOWAIT: If there is no message of the specified type, msgrv returns - 1 immediately, and errrno is set to ENOMSG.
                    0: Without a specified type of message function, it will be blocked until the following conditions occur:
                       Have a message of the specified type
                       Delete this queue from the system (error returns - 1 and errno is set to EIDRM)
                       Capture a signal and return it from the signal processor (msgrcv returns - 1, errno set to 
                        EINTR)
                 
Return value: Return part length of message if successful and - 1 if error occurs
*/

When msgrcv is successfully executed, the kernel updates the msqid_ds structure associated with the message queue to indicate the caller's process ID (msg_lrpid) and call time (msg_rtime), and decreases the number of messages in the queue (msg_qnum) by 1.
 

 

Topics: less Unix