一.消息队列
1.定义
(1) 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
(2) 每个数据块都被认为有一个数据类型, 接收者进程接收的数据可以有不同类型的数据
(3) 消息队列也有管道一样的不足, 就是每个消息的最大长度是有上限的(MSGMAX), 每个消息队列的总的字节数是有上限的(MSGMNB), 系统上消息队列的总数也有一个上限(MSGMNI)
(4) 进程通信,消息队列的生命周期随内核
(5) IPC资源用完必须删除
2.IPC对象结构(IPC是操作系统为进程提供的一种进程通信的一种方式)
struct ipc_perm
{
key_t __key; //消息队列里唯一的标示符
uid_t uid;
gid_t gid;
uid_t cuid;
unsigned short mode;//消息队列的权限
unsigned short __seq;
}
3. 消息队列的创建函数
int msgget(key_t key, int msgflg);
//其中key就是消息队列中的唯一的那个标示符
//msgflg一般是由IPC_CREAT和IPC_EXCL设置,当IPC_CREAT 和 IPC_EXCL 都设置的时候代表的是如果没有消息队列,则创建一个新的消息队列,返回这个新的消息队列的标识符, 如果已经有消息队列,则出错返回,当只设置IPC_CREAT的时候,如果没有消息队列则创建一个消息队列并且返回创建的消息队列的标识符,如果已经有消息队列,则返回原来已有的消息队列标识符
返回值:成功时返回一个非负数.失败时返回0值
4.产生消息队列标识符的函数
key_t ftok(const char* pathnaem, int project_id);
成功时返回key值,失败时返回-1,其中pathname和project_id都是有用户自己设定的
5. 消息队列的控制函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid 是msgget函数返回的标识码,cmd是将要采取的动作(IPC_STAT将nsqid_ds结构中的数据设置为消息队列的当前关联值; IPC_SET 进程有足够权限时,把消息队列的当前关联值设置为msqid_ds 数据结构中给定的值; IPC_RMID 是删除消息队列)
6. 消息队列的发送
int msgsnd(int msqid, const void* msgq, size_t msgsz, int msgflag);
msqid是msgget函数返回的标识码, msgq 是一个指向准备发送消息的一个指针, msgsz 是发送的消息的长度,这个长度不包括消息类型的长度
7.消息结构
struct msgbuf
{
long mtype;//消息类型
char mtext[1];//消息内容,大小可以自己设置
}
8.消息的接收
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msqtyp, int msgflg);
msqid:由msgget返回的标识码
msgp:指向接收消息的一个指针
msqtyp: 实现接收优先级
msgflg: 一般设置为 0
9.进程间利用消息队列通信代码展示
//common.h
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.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* msg);
int RecvMsg(int msgid, int recv_type, char out[]);
#endif //__COMM_H__
//comm.c
#include"comm.h"
int main()
{
int msgid = GetMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
printf("Please Enter# ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf));
if(s > 0)
{
buf[s - 1] = 0;
SendMsg(msgid, CLIENT_TYPE, buf);
printf("send done, wait...\n");
}
RecvMsg(msgid, SERVER_TYPE, buf);
if(strcmp(buf, "quit") == 0)
{
printf("Server quit, me too!\n");
break;
}
printf("client say# %s\n", buf);
}
return 0;
}
//client.c
#include"comm.h"
int main()
{
int msgid = GetMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
printf("Please Enter# ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf));
if(s > 0)
{
buf[s - 1] = 0;
SendMsg(msgid, CLIENT_TYPE, buf);
printf("send done, wait...\n");
}
RecvMsg(msgid, SERVER_TYPE, buf);
if(strcmp(buf, "quit") == 0)
{
printf("Server quit, me too!\n");
break;
}
printf("client say# %s\n", buf);
}
return 0;
}
//server.c
#include"comm.h"
int main()
{
int msgid = CreateMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
RecvMsg(msgid, CLIENT_TYPE, buf);
if(strcmp(buf, "quit") == 0)
{
printf("Client quit, me too!\n");
break;
}
printf("Client say# %s\n", buf);
printf("Please Enter# ");
fflush(stdout);
int s = read(0, buf, sizeof(buf));
buf[s - 1] = 0;
SendMsg(msgid, SERVER_TYPE, buf);
printf("send done, wait recv...\n");
}
DestroyMsgQueue(msgid);
return 0;
}
//Makefile
.PHONY:all
all:client server
client:client.c comm.c
gcc -g $^ -o $@
server:server.c comm.c
gcc -g $^ -o $@
.PHONY:clean
clean:
rm -rf client server
打开两个终端,监视可以看到一下结果
10.其他相关命令
查看系统当前消息队列 ipcs -q
删除系统当前消息队列 ipcrm -q msqid
注意删除消息队列时必须用root用户才可以删除