Message queue for inter-process communication in Linux (1)

Reprinted from: http://blog.csdn.net/ljianhui/article/details/10287879
Let's talk about how to use message queues for inter-process communication. Message queues have many similarities with named pipes. More about named pipes can be found in my other article: Linux Interprocess Communication - Using Named Pipes

1. What is a 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 data structures containing different types. We can avoid synchronization and blocking problems with named pipes by sending messages. But message queues, like named pipes, have a maximum length limit for each data block.

Linux uses the macros MSGMAX and MSGMNB to limit the maximum length of a message and the maximum length of a queue.

2. Using message queues in Linux
Linux provides a series of message queue function interfaces to allow us to easily use it to achieve inter-process communication. Its usage is similar to the other two System V PIC mechanisms, semaphores and shared memory.

1. msgget function
This function is used to create and access a message queue. Its prototype is:
int msgget(key_t, key, int msgflg); <span style="font-family: 宋体; background-color: rgb(255, 255, 255);"> </span>
As with other IPC mechanisms, the program must provide a key to name a particular message queue. msgflg is a permission flag that indicates the access permission of the message queue, which is the same as the access permission of the file. msgflg can be ORed with IPC_CREAT, which means that a message queue is created when the message queue named by key does not exist. If the message queue named by key exists, the IPC_CREAT flag will be ignored and only an identifier will be returned.

It returns an identifier (a non-zero integer) of the message queue named by key, or -1 on failure.

2. msgsnd function
This function is used to add a message to the message queue. Its prototype is:
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);<span style="font-family: 宋体; background-color: rgb(255, 255, 255);">  </span>
msgid is the message queue identifier returned by the msgget function.

msg_ptr is a pointer to a message to be sent, but the data structure of the message has certain requirements. The message structure pointed to by the pointer msg_ptr must be a structure that starts with a long integer member variable, and the receiving function will use this member to determine Type of message. So the message structure should be defined like this:
struct my_message{  
    long int message_type;  
    /* The data you wish to transfer*/  
};  
struct my_message{
    long int message_type;
    /* The data you wish to transfer*/
};
msg_sz is the length of the message pointed to by msg_ptr. Note that it is the length of the message, not the length of the entire structure. That is to say, msg_sz is the length of the member variable excluding the long integer message type.

msgflg is used to control what will happen when the current message queue is full or when a queued message reaches a system-wide limit.

If the call succeeds, a copy of the message data will be placed in the message queue and return 0, and return -1 on failure.

3. msgrcv function
This function is used to get messages from a message queue, its prototype is
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);  
msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。

msgtype can implement a simple receive priority. If msgtype is 0, get the first message in the queue. If its value is greater than zero, the first message with the same message type will be fetched. If it is less than zero, get the first message whose type is equal to or less than the absolute value of msgtype.

msgflg is used to control what will happen when there are no messages of the corresponding type in the queue to receive.

When the call is successful, the function returns the number of bytes placed in the receive buffer, the message is copied to the user-allocated buffer pointed to by msg_ptr, and the corresponding message in the message queue is deleted. Returns -1 on failure.

4. msgctl function
This function is used to control the message queue. It is similar to the shared memory shmctl function. Its prototype is:
int msgctl(int msgid, int command, struct msgid_ds *buf); 
 
command is the action to be taken, it can take 3 values,
    IPC_STAT: Set the data in the msgid_ds structure to the current associated value of the message queue, that is, overwrite the value of msgid_ds with the current associated value of the message queue.
    IPC_SET: If the process has sufficient privileges, set the current associated value of the message queue to the value given in the msgid_ds structure
    IPC_RMID: delete message queue

buf是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。msgid_ds结构至少包括以下成员:
struct msgid_ds  
{  
    uid_t shm_perm.uid;  
    uid_t shm_perm.gid;  
    mode_t shm_perm.mode;  
};  

成功时返回0,失败时返回-1.

三、使用消息队列进行进程间通信
马不停蹄,介绍完消息队列的定义和可使用的接口之后,我们来看看它是怎么让进程进行通信的。由于可以让不相关的进程进行行通信,所以我们在这里将会编写两个程序,msgreceive和msgsned来表示接收和发送信息。根据正常的情况,我们允许两个程序都可以创建消息,但只有接收者在接收完最后一个消息之后,它才把它删除。

接收信息的程序源文件为msgreceive.c的源代码为:
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <errno.h>  
#include <sys/msg.h>  
  
struct msg_st  
{  
    long int msg_type;  
    char text[BUFSIZ];  
};  
  
int main()  
{  
    int running = 1;  
    int msgid = -1;  
    struct msg_st data;  
    long int msgtype = 0; //注意1  
  
    //建立消息队列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  
    {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
    //从队列中获取消息,直到遇到end消息为止  
    while(running)  
    {  
        if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)  
        {  
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);  
            exit(EXIT_FAILURE);  
        }  
        printf("You wrote: %s\n",data.text);  
        //遇到end结束  
        if(strncmp(data.text, "end", 3) == 0)  
            running = 0;  
    }  
    //删除消息队列  
    if(msgctl(msgid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}  

发送信息的程序的源文件msgsend.c的源代码为:
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/msg.h>  
#include <errno.h>  
  
#define MAX_TEXT 512  
struct msg_st  
{  
    long int msg_type;  
    char text[MAX_TEXT];  
};  
  
int main()  
{  
    int running = 1;  
    struct msg_st data;  
    char buffer[BUFSIZ];  
    int msgid = -1;  
  
    //建立消息队列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  
    {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
  
    //向消息队列中写消息,直到写入end  
    while(running)  
    {  
        //输入数据  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        data.msg_type = 1;    //注意2  
        strcpy(data.text, buffer);  
        //向队列发送数据  
        if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)  
        {  
            fprintf(stderr, "msgsnd failed\n");  
            exit(EXIT_FAILURE);  
        }  
        //输入end结束输入  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
        sleep(1);  
    }  
    exit(EXIT_SUCCESS);  
}  

运行结果如下:



四、例子分析——消息类型

这里主要说明一下消息类型是怎么一回事,注意msgreceive.c文件main函数中定义的变量msgtype(注释为注意1),它作为msgrcv函数的接收信息类型参数的值,其值为0,表示获取队列中第一个可用的消息。再来看看msgsend.c文件中while循环中的语句data.msg_type = 1(注释为注意2),它用来设置发送的信息的信息类型,即其发送的信息的类型为1。所以程序msgreceive能够接收到程序msgsend发送的信息。

如果把注意1,即msgreceive.c文件main函数中的语句由long int msgtype = 0;改变为long int msgtype = 2;会发生什么情况,msgreceive将不能接收到程序msgsend发送的信息。因为在调用msgrcv函数时,如果msgtype(第四个参数)大于零,则将只获取具有相同消息类型的第一个消息,修改后获取的消息类型为2,而msgsend发送的消息类型为1,所以不能被msgreceive程序接收。重新编译msgreceive.c文件并再次执行,其结果如下:



我们可以看到,msgreceive并没有接收到信息和输出,而且当msgsend输入end结束后,msgreceive也没有结束,通过jobs命令我们可以看到它还在后台运行着。

五、消息队列与命名管道的比较

消息队列跟命名管道有不少的相同之处,通过与命名管道一样,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。

与命名管道相比,消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324365189&siteId=291194637