消息队列 (System V IPC) 详解

(1)创建或打开一个消息队列
    int msgget(key_t key, int msgflg);


(2)发送消息
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
     msqid: 是由 msgget 返回的标识符 ID 。
     msgp:  指向用户定义的缓冲区。它的第一个成员必须是一个指定消息类型的 long 型,后面跟着消息文本的内容
            struct msgbuf {
                long mtype;     /* 消息类型,必须大于0 */
                char mtext[1];  /* 消息体,不一定是字符数组,可以是任意结构*/
            };
     msgsz:消息的大小
     msgflg:IPC_NOWAIT 表示执行一个无阻塞的发送操作。
                (1)无IPC_NOWAIT标志位:
                    如果消息队列满了,msgsnd函数会陷入阻塞,直到队列有足够的空间来存放这条消息为止。
                (2)有IPC_NOWAIT标志位:
                    如果消息队列满了,msgsnd函数就不会陷入阻塞了,而是立刻返回失败,并置 errno 为 EAGAIN 。
     返回值: 成功返回消息队列ID(非负)


(3)  接收消息
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,long msgtyp,int msgflg);     
     msgtyp:
        msgtyp == 0: 最先进入消息队列的消息被取出。
        msgtyp < 0:  mtype 的值越低,其优先级越高,越早被取出。
        msgtyp > 0:,会将消息队列中第一条 mtype 值等于 msgtyp 的消息取出。通过指定不同的 msgtyp ,多个进程可以在同一个消息队列中挑选各自感兴趣的消息。一种常见的场景是各个进程提取和自己进程 ID 匹配的消息。    
     msgflg:
     ·IPC_NOWAIT :如果消息队列中不存在满足 msgtyp 要求的消息,默认情况是阻塞等待,但是一旦设置了 IPC_NOWAIT 标志位,则立即返回失败,并且设置 errno 为 ENOMSG 。
     ·MSG_EXCEPT :这个标志位是 Linux 特有的,只有当 msgtyp 大于 0 时才有意义,含义是选择 mtype ! =msgtyp 的第一条消息
     ·MSG_NOERROR :在消息体变长的情况下,此时会将消息体截断并返回。


(4)控制消息队列
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
        IPC_RMID:     删除消息队列(立即生效, 因为内核没有为使用消息队列的进程维护引用计数)
            所有阻塞的msgsnd函数和msgrcv函数会被唤醒,并返回EIDRM 错误。
        IPC_SET:    设置消息队列
        IPC_STAT:    获取消息队列的状态


(5)优缺点
    1.当消息队列中有消息到来时,无法主动通知到某进程, 必须靠进程主动去接收消息。
    2.消息队列的读取进程,只能用阻塞或轮询的方式。这就意味着一个进程或线程不得不无所事事,盯在该消息队列上。
    3.如果 System V消息队列是文件,能支持 select 、 poll 和 epoll 等I/O多路转接函数,一个进程就能同时监控多个文件(或者多个消息队列),提供更灵活的编程模式。可惜的是, System V 消息队列并非文件,不支持I/O多路转接函数。
    4.用一个消息队列可进行多个进程间的通信(通过进程接收的消息类型不同) 

(6)示例代码: 用消息队列编写了3个进程之间任意聊天的小程序

msg1.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_KEY 0x102
enum msg_type{
	T1 = 0x1,
	T2 = 0x2,
	T3 = 0x3,
};
struct msg {
	long type;
	char buf[50];
};

int main(int argc, char **argv)
{
	int ret;
	int msqid;
	struct msg smsg, rmsg;	
	char gbuf[52];
	
	printf("Usage: <1/2/3:text>\n");
	msqid = msgget(MSG_KEY, IPC_EXCL | IPC_CREAT | 0777);	// 创建消息队列
	if (msqid < 0)
	{	msqid = msgget(MSG_KEY, 777);	// 打开消息队列
		if (msqid < 0)
			perror("msgget err");
	}
	
	ret = fork();
	if (ret < 0)		// err
		return -1;
	else if (!ret)		// child	
	{	// 接收消息
		while(1)
		{	memset(&rmsg, 0, sizeof(struct msg));
			//rmsg.type = T1;
			if (msgrcv(msqid, &rmsg, sizeof(rmsg.buf), T1, 0) < 0) // 如果消息队列中没有rmsg类型的消息时, 陷入阻塞
				perror("msgrcv err");
			printf("msg1 recv: %s\n", rmsg.buf);
		}
		exit(0);
	}
	 //parent 
	while(1)
	{	
		bzero(gbuf, 52);
		memset(&smsg, 0, sizeof(struct msg));
		
		// 发送消息
		fgets(gbuf, 52, stdin);		// 从终端接收消息
		if(!strncmp(gbuf, "1:", 2))
			smsg.type = T1;
		else if(!strncmp(gbuf, "2:", 2))
			smsg.type = T2;
		else if(!strncmp(gbuf, "3:", 2))	
			smsg.type = T3;
		else
			printf("please input\n");
		
		strncpy(smsg.buf, gbuf+2, 50);
		if (msgsnd(msqid, &smsg, 50, 0) < 0)
			perror("msgsnd err");
	}
	return 0;
}



msg2.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_KEY 0x102
enum msg_type{
	T1 = 0x1,
	T2 = 0x2,
	T3 = 0x3,
};
struct msg {
	long type;
	char buf[50];
};

int main(int argc, char **argv)
{
	int ret;
	int msqid;
	struct msg smsg, rmsg;	
	char gbuf[52];
	
	printf("Usage: <1/2/3:text>\n");
	msqid = msgget(MSG_KEY, IPC_EXCL | IPC_CREAT | 0777);	// 创建消息队列
	if (msqid < 0)
	{	msqid = msgget(MSG_KEY, 777);	// 打开消息队列
		if (msqid < 0)
			perror("msgget err");
	}
	
	ret = fork();
	if (ret < 0)		// err
		return -1;
	else if (!ret)		// child	
	{	// 接收消息
		while(1)
		{	memset(&rmsg, 0, sizeof(struct msg));
			//rmsg.type = T2;
			if (msgrcv(msqid, &rmsg, sizeof(rmsg.buf), T2, 0) < 0)// 如果消息队列中没有rmsg类型的消息时, 陷入阻塞
				perror("msgrcv err");
			printf("msg2 recv: %s\n", rmsg.buf);
		}
		exit(0);
	}
	 //parent 
	while(1)
	{	
		bzero(gbuf, 52);
		memset(&smsg, 0, sizeof(struct msg));
		
		// 发送消息
		fgets(gbuf, 52, stdin);	// 从终端接收消息
		if(!strncmp(gbuf, "1:", 2))
			smsg.type = T1;
		else if(!strncmp(gbuf, "2:", 2))
			smsg.type = T2;
		else if(!strncmp(gbuf, "3:", 2))	
			smsg.type = T3;
		else
			printf("please input\n");

		strncpy(smsg.buf, gbuf+2, 50);
		
		if (msgsnd(msqid, &smsg, 50, 0) < 0)
			perror("msgsnd err");
	}
	
		
	return 0;
}


msg3.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_KEY 0x102
enum msg_type{
	T1 = 0x1,
	T2 = 0x2,
	T3 = 0x3,
};
struct msg {
	long type;
	char buf[50];
};

int main(int argc, char **argv)
{
	int ret;
	int msqid;
	struct msg smsg, rmsg;	
	char gbuf[52];
	
	printf("Usage: <1/2/3:text>\n");
	msqid = msgget(MSG_KEY, IPC_EXCL | IPC_CREAT | 0777);	// 创建消息队列
	if (msqid < 0)
	{	msqid = msgget(MSG_KEY, 777);	// 打开消息队列
		if (msqid < 0)
			perror("msgget err");
	}
	
	ret = fork();
	if (ret < 0)		// err
		return -1;
	else if (!ret)		// child	
	{	// 接收消息
		while(1)
		{	memset(&rmsg, 0, sizeof(struct msg));
			//rmsg.type = T3;
			if (msgrcv(msqid, &rmsg, sizeof(rmsg.buf), T3, 0) < 0)// 如果消息队列中没有rmsg类型的消息时, 陷入阻塞
				perror("msgrcv err");
			printf("msg3 recv: %s\n", rmsg.buf);
		}
		exit(0);
	}
	 //parent 
	while(1)
	{	
		bzero(gbuf, 52);
		memset(&smsg, 0, sizeof(struct msg));
		
		// 发送消息
		fgets(gbuf, 52, stdin);	// 从终端接收消息
		if(!strncmp(gbuf, "1:", 2))
			smsg.type = T1;
		else if(!strncmp(gbuf, "2:", 2))
			smsg.type = T2;
		else if(!strncmp(gbuf, "3:", 2))	
			smsg.type = T3;
		else
			printf("please input\n");

		strncpy(smsg.buf, gbuf+2, 50);
		
		if (msgsnd(msqid, &smsg, 50, 0) < 0)
			perror("msgsnd err");
	}
	
		
	return 0;
}



执行结果:

        (1)在3个终端中分别执行./msg1 ./msg2 ./msg3
        (2)输入:
            在终端1中分别输入:
                2:nihao woshi pro1
                3:nihao woshi pro1
            在终端2中分别输入:
                1:nihao woshi pro2
                3:nihao woshi pro2
            在终端3中分别输入:
                1:nihao woshi pro3
                2:nihao woshi pro3
        (3)执行结果
            // 终端1
            book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/msg$ ./msg1
            Usage: <1/2/3:text>
            2:nihao woshi pro1
            3:nihao woshi pro1
            msg1 recv: nihao woshi pro2
            msg1 recv: nihao woshi pro3
            // 终端2
            book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/msg$ ./msg2
            Usage: <1/2/3:text>
            msg2 recv: nihao woshi pro1
            1:nihao woshi pro2
            3:nihao woshi pro2
            msg2 recv: nihao woshi pro3
           
            // 终端3
            book@gui_hua_shu:$ ./msg3
            Usage: <1/2/3:text>
            msg3 recv: nihao woshi pro1
            msg3 recv: nihao woshi pro2
            1:nihao woshi pro3
            2:nihao woshi pro3

猜你喜欢

转载自blog.csdn.net/qq_38813056/article/details/85224485