消息队列是消息的链表,存放在内存中,由内核维护;
1. 消息队列中的消息是有类型的;
2. 消息队列中的消息是有格式的;
3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取;
4. 消息队列允许一个或多个进程向它写入或读取消息;
5. 与无名管道、命名管道一样,从消息队列中读取消息,消息队列中对应的消息将被删除;
6. 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的;
7. 只有内核重启或人工删除消息队列时,该消息队列才会被删除;若不人工删除消息队列,消息队列将一直存在于系统中
System V提供的IPC通信机制需要一个key值,通过key值就可以在系统内获得一个唯一的消息队列标识符。key值可以人工指定,也可以通过ftok函数获得
#include
#include
key_t ftok(const char *pathname, int proj_id);
获得项目相关的唯一的IPC键值
pathname:路径名
proj_id:项目ID,非0整数(只有低8位有效)
成功:返回key值;
失败:返回-1
#include
int msgget(key_t key, int msgflg);
创建一个新的或者打开一个已经存在的消息队列。不同进程调用此函数,只有用相同的key值就能得到同一个消息队列的标识符
key:IPC的key值
msgflg:标识函数的行为以及消息队列的权限
msgflg的取值:
IPC_CREAT:创建消息队列
IPC_EXCL:检测消息队列是否存在
位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open函数的mode_t一样,但可执行权限未使用
成功:返回消息队列的标识符;
失败:返回-1
[root@ansible9 ~]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x01000080 0 root 666 0 0
消息队列的信息格式是需要人工定义的,格式放在一个结构体中;
信息结构体中的第一个long型的成员的作用是,只有对这类信息感兴趣的进程才能拿到这类信息
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:消息队列标识符
msgp:要是的消息的结构体变量的地址
msgsz:消息正文的字节数(等于消息结构体的大小减去long类型的大小)
msgflg:函数的控制属性
0:msgsnd调用阻塞直到条件满足为止
IPC_NOWAIT:若消息没有立刻发送则调用该函数的进程会立刻返回
成功:0
失败:-1
#include
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msqid:消息队列的标识符,代表要从哪个消息队列中获取消息
msgp:存放消息的结构体地址
msgsz:消息正文的字节数
msgtyp:感兴趣的消息类型,可以有以下几种类型
msgtyp=0:返回队列中第一个消息
msgtyp>0:返回队列中消息类型为msgtyp的消息
msgtyp<0:返回队列中消息类型小于等于msgtyp的绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
msgflg:函数的控制属性
0:msgrcv调用阻塞直到接收消息成功为止
MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截断到nbytes字节,且不通知消息发送进程;
IPC_NOWAIT:调用进程会立即返回,若没有收到消息则立即返回-1
若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的;
在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出;
成功:返回读取消息的长度;
失败:返回-1
#include
#include
#include
#include
#include
#include
int main(int arg, char *argv[])
{
//获取IPC唯一key值key
key_t key = ftok("/",1);
printf("%#x\n",key);
//创建一个消息队列,队列标识符为msg_id
int msg_id = msgget(key,IPC_CREAT|0666);
printf("%d\n",msg_id);
//定义消息队列的信息格式MSG
typedef struct msg
{
long type;
char text[100];
int a;
char name[32];
}MSG;
//新建一个信息
MSG msg;
memset(&msg,0,sizeof(msg));
msg.type=10;
msg.a = 100;
strcpy(msg.name,"andy");
strcpy(msg.text,"ni hao");
//发送上面新建的信息
msgsnd(msg_id, &msg, sizeof(MSG)-sizeof(long),0);
return 0;
}
收:
#include
#include
#include
#include
#include
#include
int main(int arg, char *argv[])
{
//获取IPC唯一key值key
key_t key = ftok("/",1);
printf("%#x\n",key);
//创建一个消息队列,队列标识符为msg_id
int msg_id = msgget(key,IPC_CREAT|0666);
printf("%d\n",msg_id);
//定义消息队列的信息格式MSG
typedef struct msg
{
long type;
char text[100];
int a;
char name[32];
}MSG;
//接收消息
MSG msg;
memset(&msg,0,sizeof(msg));
msgrcv(msg_id, &msg, sizeof(MSG)-sizeof(long), 10,0);
printf("发送者:%s\n", msg.name);
printf("消息:%s\n", msg.text);
return 0;
}
不管是发送者还是接收者
1. ftok得到唯一key
2. msgget创建消息队列
发送者
3. MSG msg;
4. msg.mtype=接收感兴趣的类型值;
5. msgsnd(msg_id, &msg, sizeof(MSG)-sizeof(long), 0);//发送消息到消息队列
接收者
1. MSG msg;
2. msgrcv(msg_id, &msg, sizeof(MSG)-sizeof(long), 接收感兴趣的类型值, 0);
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid:消息队列的标识符
cmd:函数功能的控制
buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性
cmd函数功能的控制:
IPC_RMID:删除由msqid标识的消息队列,将他从系统中删除并破坏相关的数据结构
IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中
IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值
成功:返回0;
失败:返回-1
int w = msgctl(msg_id,IPC_RMID,NULL);
#include
#include
#include
#include
#include
#include
#include
//定义人名和消息类型的对应关系
char *peple[3] = {
"andy:10",
"linda:20",
"tony:30"
};
long type = 0; //消息类型
//定义消息结构体
typedef struct _msg{
long mtype; //接收者类型
char content[100]; //发送的消息
char name[20]; //发送者姓名
}MSG;
//标记我是谁
char me[20]="";
int main(int argc, char * kwargs[])
{
//获取系统ipc key
key_t key = ftok("/",100);
//创建消息队列
int msg_id = msgget(key, IPC_CREAT|0666);
printf("消息id:%d\n",msg_id);
//创建两个子进程一个发消息,一个收消息
int i = 0;
for(;i<2;i++)
{
pid_t pid = fork();
if (pid == 0)
break;
}
if(i==0) //第一个子进程负责发送消息
{
while(1)
{
char who[32] = "";
printf("给谁发送消息:");
fgets(who,sizeof(who),stdin);
who[strlen(who)-1]='\0';
strcat(who,":");
for(int n=0;n<3;n++)
{
if(strncmp(who,peple[n],strlen(who))==0)
{
char *tmp1;
char name[10]="";
memcpy(name,peple[n],strlen(peple[n]));
tmp1 = strtok(name,":");
tmp1 = strtok(NULL,":");
type = atol(tmp1);
break;
}
}
char msg[128] = "";
printf("请输入要发送的消息:");
fgets(msg,sizeof(msg),stdin);
msg[strlen(msg)-1]='\0';
//将消息放入消息队列
MSG new_msg;
new_msg.mtype = type;
memcpy(new_msg.content,msg,strlen(msg));
#ifdef ANDY
memcpy(new_msg.name,"andy",sizeof("andy"));
#endif
#ifdef LINDA
memcpy(new_msg.name,"linda",sizeof("lnda"));
#endif
#ifdef TONY
memcpy(new_msg.name,"tony",sizeof("tony"));
#endif
int res = msgsnd(msg_id,&new_msg,sizeof(MSG)-sizeof(long),0);
}
}
else if(i==1) //第二个子进程负责接收消息
{
while(1)
{
MSG new_msg;
char who[20];
#ifdef ANDY
memcpy(who,"andy:",strlen("andy:"));
#endif
#ifdef LINDA
memcpy(who,"linda:",strlen("linda:"));
#endif
#ifdef TONY
memcpy(who,"tony:",strlen("tony:"));
#endif
for(int n=0;n<3;n++)
{
if(strncmp(who,peple[n],strlen(who)-1)==0)
{
char *tmp1;
char name[10]="";
memcpy(name,peple[n],strlen(peple[n]));
tmp1 = strtok(name,":");
tmp1 = strtok(NULL,":");
type = atol(tmp1);
break;
}
}
//接收消息
int res = msgrcv(msg_id,&new_msg,sizeof(MSG)-sizeof(long),type,0);
printf("\r%s说:%s\n",new_msg.name,new_msg.content);
printf("给谁发消息: ");
fflush(NULL);
}
}
else if(i==2) //父进程负责子进程资源回收
{
while(1)
{
pid_t pid = waitpid(-1,NULL,WNOHANG);
if(pid>0)
{
printf("子进程%d退出了\n",pid);
}
else if(pid==0)
{
continue;
}
else if(pid == -1)
{
break;
}
}
}
return 0;
}