(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