消息队列与管道的异同点:
消息队列与管道的相同点:
1.都是利用内核的1G空间来通信(如图);
消息队列与管道的不同点:
1.管道的数据读取是严格按照先进先出;
2.消息队列的数据读取可以按照数据类型进行读取;
消息队列函数
一、int msgget(key_t key, int msgflg);
1.功能:创建或打开一个消息队列
2.参数:
key:两种方式:1-由ftok函数获得;2-直接填写IPC_PRIVATE(此时系统会分配一个唯一的key值)
msgflg:打开的方式
IPC_CREAT:没有消息队列的话,创建一个新的消息队列,如果已有有相同msgid的消息队列,则返回已有消息队列的msgid;
IPC_EXCL:不可单独使用。与IPC_CREAT结合使用时(IPC_CREAT|IPC_EXCL),如果已有相同msgid的消息队列存在,则返回一个EEXIST错误,因为已存在;
读写权限:与文件权限一样。(IPC_CREAT | IPC_EXCL | 0664)
3.返回值:
成功:返回相应的msgid值;
错误:返回-1;
二、int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
1.功能:向值为msgid的消息队列发送一个消息
2.参数:
msgid:目标消息队列的msgid值
msgq:要发送消息的结构体地址
msgsz:要发送的消息大小,结构体总大小减去消息类型大小 sizeof(struct msgbuf) - sizeof(long)
msgflg:以阻塞(0)或者非阻塞(IPC_NOWAIT)的模式发送
3.返回值:
成功:返回 0
失败:返回 -1
要发送消息的结构体模板
struct msgbuf{
long msgtype; //大于0
消息正文,可以自定义类型
}
三、int msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
1.功能:从消息队列读取一条消息
2.参数:
msgid:同msgsnd();
msgp:要存放接收到消息的结构体地址
msgsz:要接收正文的长度sizeof(struct msgbuf) - sizeof(long)
msgtyp:接收消息的类型,区别于管道的这个关键点。
msgtyp = 0:按先进先出逐个读取数据,与管道无异;
msgtyp > 0:表示要接收类型为msgtyp的消息;
msgtyp < 0:表示要接收类型为 [ 小于(msgtyp的绝对值中)] 最小的类型的消息
3.返回值:
成功:返回 0
失败:返回 -1
四、int msgctl(int msqid, int cmd, struct msqid_ds *buf);
1.功能:控制消息队列
2.参数:
msqid:消息队列msgid号;
cmd:控制命令 IPC_STAT / IPC_SET / IPC_RMID
IPC_STAT:获取消息队列的属性,并放在第三个参数(struct msqid_ds *buf)内;
IPC_SET:设置消息队列的属性,要设置的属性放在第三个参数;
IPC_RMID:删除消息队列,此时第三个参数为NULL;
3.返回值:
成功:返回 0
失败:返回 -1
代码实例
下面咱们来写几个具体代码验证一下消息队列的特点;
第一个代码:从一个 terminal 输入,从另一个 terminal 读取并打印:(半双工通信方式)
/*在终端输入消息*/
#include "msg.h"
int main(int argc, const char *argv[])
{
key_t key;
struct msgbuf sendbuf; //声明要发送的消息变量
size_t sndsz = sizeof(sendbuf) - sizeof(long); //计算要发送的消息的大小,用作发送函数msgsnd的第3个参数
int msgid;
//生成key值
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
printf("key = %#0x\n", key); //打印key值,用十六进制打印
if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST) //如果错误值是EEXIST,意思就是这个消息队列已经存在的话,就执行下面的代码
{
msgid = msgget(key, 0664); //不创建,只打开消息id为msgid的消息队列
printf("messageid %d -- key %#0x is exist\n", msgid, key);
}
else
{
perror("msgget error");
exit(1);
}
}
else
{
printf("msgid: %d -- key: %#0x\n is created\n", msgid, key);
}
system("ipcs -q");
sendbuf.msgtype = 1; //给这个sendbuf变量定义一个类型,用于接收方选择性接收
while(fgets(sendbuf.buf, N, stdin) != NULL)
{
msgsnd(msgid, &sendbuf, sndsz, 0);
system("ipcs -q");
if(strncmp(sendbuf.buf, "quit", 4) == 0)
{
break;
}
}
return 0;
}
/*从终端读取并打印消息*/
#include "msg.h"
int main(int argc, const char *argv[])
{
key_t key;
int msgid;
struct msgbuf rcvbuf;
size_t rcvsz = sizeof(rcvbuf) - sizeof(long);
ssize_t rcvnum;
char sysbuf[N];
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
printf("key = %#0x\n", key);
if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST)
{
msgid = msgget(key, 0664);
printf("messageid %d -- key %#0x is exist\n", msgid, key);
}
else
{
perror("msgget error");
exit(1);
}
}
else
{
printf("msgid: %d -- key: %#0x\n is created\n", msgid, key);
}
rcvbuf.msgtype = 1;
//注意msgrcv的第4个参数是要接收的消息的类型,如果是0的话,就是按先进先出的方式接收,此时和管道一样;
while( (rcvnum = msgrcv(msgid, &rcvbuf, rcvsz, 0, 0)) > 0)
{
if(strncmp(rcvbuf.buf, "quit", 4) == 0)
{
break;
sprintf(sysbuf, "ipcrm -q %d", msgid);
system(sysbuf);
system("ipcs -q");
}
system("ipcs -q");
fputs(rcvbuf.buf, stdout);
}
return 0;
}
Makefile:利用了 Makefile 的隐晦规则以及伪目标;
.PHONY:
all
clean
all:write read
write:
read:
clean:
-rm write read
第二个代码:建立两个 terminal ,并且每个 terminal 均可以向对方发生消息,同时接收对方发送过来的消息(全双工通信模式)
注意发送和接收方的消息类型,这里容易出错的。
#include "msg.h"
int main(int argc, const char *argv[])
{
key_t key;
int msgid, pid;
struct msgbuf sendbuf;
size_t sndsz = sizeof(sendbuf) - sizeof(long);
struct msgbuf recvbuf;
size_t rcvsz = sizeof(recvbuf) - sizeof(long);
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST)
{
msgid = msgget(key, 0664);
printf("key:%#0x msgid:%d --message queue is exist\n", key, msgid);
}
else
{
perror("msgget error");
kill(getpid(), SIGKILL);
}
}
if( (pid = fork()) < 0)
{
perror("fork error");
exit(1);
}
else if(pid == 0)
{
sendbuf.msgtype = 2;
while(fgets(sendbuf.buf, N, stdin) != NULL)
{
msgsnd(msgid, &sendbuf, sndsz, 0);
if(strncmp(sendbuf.buf, "quit", 4) == 0)
{
kill(pid, SIGKILL);
exit(5);
}
}
raise(SIGKILL);
}
else if(pid > 0)
{
int status, out_pid;
recvbuf.msgtype = 2;
while(msgrcv(msgid, &recvbuf, rcvsz, 1, 0) > 0)
{
if(strncmp(recvbuf.buf, "quit", 4) == 0)
{
exit(5);
}
fputs(recvbuf.buf, stdout);
}
while(1)
{
out_pid = waitpid(-1, &status, WNOHANG);
}
if(WIFEXITED(status))
{
printf("over_process, pid = %d, exitnum = %d\n", out_pid,
WEXITSTATUS(status));
}
}
return 0;
}
#include "msg.h"
int main(int argc, const char *argv[])
{
key_t key;
int msgid, pid;
struct msgbuf sendbuf;
size_t sndsz = sizeof(sendbuf) - sizeof(long);
struct msgbuf recvbuf;
size_t rcvsz = sizeof(recvbuf) - sizeof(long);
if( (key = ftok(".", 1)) < 0)
{
perror("ftok error");
exit(1);
}
if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0)
{
if(errno == EEXIST)
{
msgid = msgget(key, 0664);
printf("key:%#0x msgid:%d --message queue is exist\n", key, msgid);
}
else
{
perror("msgget error");
kill(getpid(), SIGKILL);
}
}
if( (pid = fork()) < 0)
{
perror("fork error");
exit(1);
}
else if(pid == 0)
{
sendbuf.msgtype = 1;
while(fgets(sendbuf.buf, N, stdin) != NULL)
{
msgsnd(msgid, &sendbuf, sndsz, 0);
if(strncmp(sendbuf.buf, "quit", 4) == 0)
{
kill(pid, SIGKILL);
exit(5);
}
}
raise(SIGKILL);
}
else if(pid > 0)
{
int status, out_pid;
recvbuf.msgtype = 1;
while(msgrcv(msgid, &recvbuf, rcvsz, 2, 0) > 0)
{
if(strncmp(recvbuf.buf, "quit", 4) == 0)
{
exit(5);
}
fputs(recvbuf.buf, stdout);
}
while(1)
{
out_pid = waitpid(-1, &status, WNOHANG);
}
if(WIFEXITED(status))
{
printf("over_process, pid = %d, exitnum = %d\n", out_pid,
WEXITSTATUS(status));
}
}
return 0;
}