IPC message queue

Keywords: Unix Linux

I. What is a message queue
Message queue:
Message queuing provides a way to send a data block from one process to another. Each data block is considered to have a type. The data block received by the recipient process can have different type values. We can avoid the synchronization and blocking problems of named pipes by sending messages. So message queuing is message-based and pipeline is byte stream-based. .
The same disadvantage of message queues and named pipelines is that the maximum length of each message is a maximum (MSGMAX), the total number of bytes per message queue is a maximum (MSGMNB), and the total number of message queues on the system is also a maximum (MSGMNI).

msgmax, the maximum length of each message is limited.


msgmnb, the maximum number of bytes per message queue is also limited


msgmni, how many message queues can exist on each system, has its own limitations

II. Structure of message queue
IPC is the general term of inter-process communication in UNIX system. There are three kinds of IPC called XSI: message queue, semaphore and shared memory. Every creation of any of these three is equivalent to the creation of an XSI IPC object. The system maintains a structure ipc_perm for each IPC object
This is the structure of the message queue, with a header file named msg.h under (/ usr/include/linux).

struct msqid_ds {
  struct ipc_perm msg_perm;
  struct msg *msg_first;      /* first message on queue,unused  */
  struct msg *msg_last;       /* last message in queue,unused */
  __kernel_time_t msg_stime;  /* last msgsnd time */
  __kernel_time_t msg_rtime;  /* last msgrcv time */
  __kernel_time_t msg_ctime;  /* last change time */
  unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
  unsigned long  msg_lqbytes; /* ditto */
  unsigned short msg_cbytes;  /* current number of bytes on queue */
  unsigned short msg_qnum;    /* number of messages in queue */
  unsigned short msg_qbytes;  /* max number of bytes on queue */
  __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
  __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */

};

ipc_prem:

struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
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 */
};

The first member _key, whose type is key_t. This is the system's unique identifier for IPC resources. This identifier must be obtained before applying for IPC resources. We can get it through the ftok function.
Functions of message queues
Get message queue:

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

If successful, return msgid of a message queue as the unique identifier of the process; fail to return - 1
key is a port number that can also be generated by the function ftok, identifying the only message queue.
ftok function:

key_t ftok(const char *pathname, int proj_id);  

The function ftok converts an existing path name and an integer identifier (low order 8 bits with proj_id) into a key_t value, called the IPC key.
flag is the way to create (IPC_CREAT and IPC_EXCL):
IPC_CREAT is used separately to indicate that an application for the creation of an IPC resource is obtained directly if the requested resource already exists; if not, a new IPC resource is created.

IPC_CREAT is used with IPC_EXCL to indicate an application for creating an IPC resource. If the requested resource does not exist, a new IPC resource is created; if it already exists, it returns to -1.

IPC_EXCL does not make any sense to use alone. Its existence is to use it with IPC_CREAT to ensure that a newly created resource is available.

Destroy message queues:

int msgctl(int msgid,int cmd,struct msqid_ds *buf)

The first parameter msgid is the return value of msgget. The second parameter, cmd, specifies the command to be executed on the message queue. msgctl not only has the function of deleting.

cmd:
IPC_STAT, gets the msqid_ds structure of this message queue and stores it in the structure pointed to by buf.

IPC_SET copies some fields in parameter buf to corresponding fields in message queue msqid_ds structure. This command can only be executed by two users, one is that its valid user ID is equal to msg_perm.cuid and the other is msg_perm.uid. The other is superuser root.

IPC_RMID, deletes the message queue and all data of the message queue from the system. Deletion takes effect immediately.
So if we execute the deletion action, we can set the cmd to IPC_RMID.

Send a message:

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

Return value: Successful return of 0, Failure Return of - 1 nbytes as the size of the transmitted data
ptr refers to a structure with the following structure:

struct msgbuf
{
    long mtype;
    char mtext[MYSIZE];
};

mtype is set to the type of data to be sent, and mtext stores the data to be sent.

Flag can be set to IPC_NOWAIT or not (set to 0). IPC_NOWAIT is similar to the non-blocking I/O flag of file I/O. When the message queue is full and IPC_NOWAIT is specified, msgsnd immediately returns to EAGAIN in error, and if not specified, the process will be blocked all the time. Until the message queue has space to hold the sent message or the system deletes the message queue or receives a signal and returns from the signal processing program.

4. Code of message queue
comm.h:

#ifndef COMM_H__  
#define COMM_H__  

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

#define PATHNAME "."  
#define PROJ_ID 0x6666  

#define SERVER_TYPE 1  
#define CLIENT_TYPE 2  

struct msgbuf  
{  
    long mtype;  
    char mtext[1024];  
};  

int CreateMsgQueue();   
int GetMsgQueue();   
int DestroyMsgQueue(int msgid);   
int SendMsg(int msgid,int who,char in[]);    
int RecvMsg(int msgid,int recvType,char out[]);   

#endif  

comm.c:

#include "comm.h"  

static int commMsgQueue(int flags)  
{  
    key_t _key=ftok(PATHNAME,PROJ_ID);     
    if(_key<0)  
    {  
        perror("ftok");  
        return -1;  
    }  
    else  
    {  
        int msgid=msgget(_key,flags);     
        if(msgid<0)  
        {  
            perror("msgget");  
            return -2;  
        }  
        else  
        {  
            return msgid;  
        }  
    }  
}  

int CreateMsgQueue()  
{  
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);  
}  

int GetMsgQueue()  
{  
    return commMsgQueue(IPC_CREAT|0666);  
}  

int DestroyMsgQueue(int msgid) 
{  
    if(msgctl(msgid,IPC_RMID,NULL)<0)    
    {  
        perror("msgctl:Destroy");  
        return -1;  
    }  
    return 0;  
}  

int SendMsg(int msgid,int who,char in[])    
{  
    struct msgbuf buf;  
    buf.mtype=who;  
    strcpy(buf.mtext,in);  
    if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)  
    {  
        perror("msgsnd");  
        return -1;  
    }  
    return 0;  
}  

int RecvMsg(int msgid,int recvType,char out[])    
{  
    struct msgbuf buf;  
    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0)   
    {  
        perror("msgrcv");  
        return -1;  
    }  
    strcpy(out,buf.mtext);  
    return 1;  
}  

Sender code server.c:

#include "comm.h"  
int main()  
{  
    int msgid=CreateMsgQueue();   
    //printf("getting success:%d\n",msgid);  
    char buf[1024];  
    while(1)  
    {  
        int ret=RecvMsg(msgid,CLIENT_TYPE,buf);    
        if(ret<0)  
        {  
            break;  
        }  
        printf("CLIENT_TYPE#%s\n",buf);  

        buf[0]=0;  
        printf("PLease Enter#");  
        fflush(stdout);  

        size_t s=read(0,buf,sizeof(buf));  
        if(s>0)  
        {  
            buf[s-1]=0;  
            SendMsg(msgid,SERVER_TYPE,buf);   
            printf("send done,wait recv...\n");  
        }  
    }  

    DestroyMsgQueue(msgid);  
    return 0;  
}  

Receiver code client.c:

#include "comm.h"  
int main()  
{  
    int msgid=GetMsgQueue();    
    //printf("getting success:%d\n",msgid);  
    char buf[1024];  
    while(1)  
    {  
        buf[0]=0;  
        printf("PLease Enter#");  
        fflush(stdout);  

        size_t s=read(0,buf,sizeof(buf));  
        if(s>0)  
        {  
            buf[s-1]=0;  
            SendMsg(msgid,CLIENT_TYPE,buf);    
            printf("send done,wait recv...\n");  
        }  

        RecvMsg(msgid,SERVER_TYPE,buf);   
        printf("server#%s\n",buf);  
    }  
    return 0;  
}  

Posted by coelex on Tue, 25 Jun 2019 15:14:13 -0700