Linux进程间的通信方式 -———消息队列。
消息队列和共享内存类似
消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息。
消息队列存在于系统内核中,消息的数量受系统限制。
我们来看一下有关消息队列的函数。
msgget();msgsnd();msgrcv();msgctl();
第一个函数:
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
功能:创建一个消息队列或取得一个已经存在的消息队列。
返回值:成功返回消息队列的标示符(ID),失败为-1;
参数:
_key :
1. 消息队列的键值,为IPC_PRIVATE 意思就是创建一个只能被创建进程读写的消息队列。
2. 不是IPC_PRIVATE .则我们可以指定一个值6666,还可以用ftok();函数来获得一个唯一的键值。(ftok()函数见其它文章);
_msgflg : 创建消息队列的创建方式或权限。
创建方式有:IPC_CREAT 如果内存不存在则创建一个消息队列,否则取得。
IPC_EXCL 只有不消息队列存在的时候,新的消息队列才会被创建,否则会产生错误。
Ok,看个例子:
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#define MYMSG_KEY 6666
int main(int argc ,char *argv[])
{
int msgid;
msgid=msgget(ftok(".",100),IPC_CREAT | 0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT | 0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error :");
exit(EXIT_FAILURE);
}
return 0;
}
运行结果如下:
我们可以用ipcs -q 来查看是否创建成功。
用ipcrm –q msgid号
==========================================================================
第二个函数:
#include<sys/msg.h>
Int msgsnd(int msgid,struct msgbuf *msgp ,int msgsz, int msgflg);
功能:往队列中发送一个消息。
返回值:成功返回0,失败返回-1;
参数:
msgid 消息表识id 也就是msgget()函数的返回值。
msgp :指向消息缓冲区的指针,该结构体为
struct mymesg {
long mtype; /* positive message type 消息的类型*/
char mtext[512]; /* message data, of length nbytes 存放消息数据内容*/
};
msgsz, :是消息的字节大小,不包含消息类型的长度(4个字节)
msgflg :可以设置为0;或者使用IPC_NOWAIT,如果消息队列已经满了,那么次消息就不会写到消息队列中, 控制将返回到对于进程中。如果没有指明,调用进程会被挂起,直到消息可以写到消息队列中。
看一个一个例子:
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#define MYMSG_KEY 6666
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
int main(int argc ,char *argv[])
{
int msgid,msgsend_ret;
char buf[25];
struct mymesg msg_send;
msgid=msgget(ftok(".",100),IPC_CREAT | 0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT | 0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error :");
exit(EXIT_FAILURE);
}
// write messages to msg queue
msg_send.mtype=1;
printf("enter a message:\n");
gets(buf);
strcpy(msg_send.mtext,buf);
msgsend_ret=msgsnd(msgid,&msg_send,strlen(msg_send.mtext)+1,0);
if(msgsend_ret==-1)
{
perror("msgget error: ");
exit(EXIT_FAILURE);
}
return 0;
}
运行结果:在此我们写入2个消息
我们可以看到消息的数量为2
==================================================================
既然我们向消息队列中写入了那么我们就可以读取消息队列中的消息了。
那就要用到msgrcv();
格式:#include<sys/msg.h>
int msgrcv(int msgid , struct msgbuf *msgp,int msgsz , long mtype,int msgflg );
功能: 读取消息,从消息队列中读走消息,是读走不是读取,也就是读完之后没有了,这种机制类似于管道。
返回值:成功返回0,失败返回-1;
参数:
msgid : 消息队列的id号
msgp :是我们要读到的消息数据存储消息的地址。
msgsz 是消息的长度不包含mtype 的长度 ,我们可以这样算:
msgsz=sizeof(struct msgbuf)-sizeof(long);
mtype 是我们要从消息队列中读取的消息的类型。如果此值为0,则会读取时间最长的那一条消息,不论是什么类型,也就是我们第一个写入消息队列中的消息。
msgflg 可以设置为0,代表该进程将一直阻塞,直到有消息可读停止。但我们可以吧该值设为IPC_NOWAIT 表示,如果没有消息可读时,则立刻返回-1,进程被挂起。
看例子:
我们把刚才写的2个消息在读出来。
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#define MYMSG_KEY 6666
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
int main(int argc ,char *argv[])
{
int msgid,msgsend_ret;
char buf[25];
struct mymesg msg_send;
msgid=msgget(ftok(".",100),IPC_CREAT | 0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT | 0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error :");
exit(EXIT_FAILURE);
}
// write messages to msg queue
/* 0 msg_send.mtype=1;
printf("enter a message:\n");
gets(buf);
strcpy(msg_send.mtext,buf);
msgsend_ret=msgsnd(msgid,&msg_send,strlen(msg_send.mtext)+1,0);
if(msgsend_ret==-1)
{
perror("msgget error: ");
exit(EXIT_FAILURE);
}
*/
//read mseeags from msg queue
int msgrcv_ret;
struct mymesg mymsgrece;
msgrcv_ret=msgrcv(msgid,&mymsgrece, sizeof(struct mymesg)-sizeof(long),1,0);
//读取消息的类型为1,长度为sizeof(struct mymesg)-sizeof(long)把读到
//的消息放到mymsgrece这这结构体中。
if(msgrcv_ret==-1)
{
perror("msgrcv error:");
exit(EXIT_FAILURE);
}
printf("received msg from queue : %s\n",mymsgrece.mtext);
return 0;
}
运行结果:我们执行两次,把两个刚才写的两个消息都读出来。
从结果中我们看得出:
1. 先写进去的先读出来,即遵循先进先出的原则。
2. 当我们尝试再去读时,它会阻塞。
3. 用ipcs -q查看她真是遵循读走就没有了的原则。
==========================================================
第四个函数,既消息队列进行处理函数,比如删除消息队列。
还可以进行获取消息队列的详细信息,改变消息队列的信息等。
格式:
#include<sys/msg.h>
int msgctl(int msgid, int cmd ,struct msgqid _ds *buf);
功能:对消息队列的控制处理函数。
返回值:成功返回0,失败返回-1.
参数:
msgid 消息队列的ID,也就是msgget()函数的返回值。
cmd 命令,对消息队列的处理
IPC_RMID 从系统内核中删除消息队列,相当于我们在终端输入ipcrm -q 消息队列id
IPC_STAT 获取消息队列的详细消息。包含权限,各种时间,id等
IPC_SET 设置消息队列的信息。
buf: 存放消息队列状态的结构体的地址。
看个例子:
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#define MYMSG_KEY 6666
int main(int argc ,char *argv[])
{
int msgid,msgctl_ret;
msgid=msgget(ftok(".",100),IPC_CREAT | 0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT | 0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error :");
exit(EXIT_FAILURE);
}
//delete queue
/* msgctl_ret = msgctl(msgid,IPC_RMID,0);
if(msgctl_ret==-1)
{
perror("msgctl error :");
exit(EXIT_FAILURE);
}
printf("deleted queue %d ok.\n ",msgid);
*/
return 0;
}
我们先把msgct函数注释,创建出一个消息队列,然后把创建的注释,启用msgct函数。
运行结果如下:
我们把第二个参数改成IPC_STAT 获取消息队列的详细消息。包含权限,各种时间,id等
例子:
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#define MYMSG_KEY 6666
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
void msg_stat(int msgid,struct msqid_ds msg_info)
{
int ret;
ret = msgctl(msgid,IPC_STAT,&msg_info);
if(ret==-1)
{
perror("msgctl error :");
exit(EXIT_FAILURE);
}
printf("\n");
printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
printf(" max of bytes on queue id %d\n",msg_info.msg_qbytes);
printf(" number of messages in queue is %d\n",msg_info.msg_qnum);
printf("last change time is %s\n",ctime(&msg_info.msg_ctime));
printf("message uid is %d\n",msg_info.msg_perm.uid);
printf("message gid is %d\n",msg_info.msg_perm.gid);
}
int main(int argc ,char *argv[])
{
char buf[25];
int msgid,msgsend_ret,msgctl_ret;
struct mymesg msg_send;
msgid=msgget(ftok(".",100),IPC_CREAT | 0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT | 0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error :");
exit(EXIT_FAILURE);
}
// write messages to msg queue
msg_send.mtype=1;
printf("enter a message:\n");
gets(buf);
strcpy(msg_send.mtext,buf);
msgsend_ret=msgsnd(msgid,&msg_send,strlen(msg_send.mtext)+1,0);
if(msgsend_ret==-1)
{
perror("msgget error: ");
exit(EXIT_FAILURE);
}
struct msqid_ds msg_ginfo;
msg_stat(msgid,msg_ginfo);
return 0;
}
运行结果:
Ok.我们综合用一下这4个函数
msgget() 类似文件操作函数open();
msgsnd(); 类似文件操作函数write();
msgrcv (); 类似文件操作函数read();
msgctl(); 类似文件操作函数close();
模拟银行的取号系统:
源程序1(取号机);
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#define MYMSG_KEY 6666
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
int main()
{
int msgid,msgsend;
struct mymesg mymsgsend;
msgid=msgget(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error: ");
exit(EXIT_FAILURE);
}
mymsgsend.mtype=1;
while(1)
{
printf("enter a type and a msg use to send :\n");
scanf("%d%s",&mymsgsend.mtype,mymsgsend.mtext);
msgsend=msgsnd(msgid,&mymsgsend,strlen(mymsgsend.mtext)+1,0);
if(msgsend==-1)
{
perror("msgget error: ");
exit(EXIT_FAILURE);
}
if(strcmp(mymsgsend.mtext,"exit")==0) break;
}
return 0;
}
源程序2(综合业务):
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#define MYMSG_KEY 6666
struct mymesg {
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
int main()
{
int msgid,msgrcv_ret,msgctl_ret;
struct mymesg mymsgrece;
msgid=msgget(MYMSG_KEY ,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msgget error:");
exit(EXIT_FAILURE);
}
while(1)
{
scanf("%d",&mymsgrece.mtype);
msgrcv_ret=msgrcv(msgid,&mymsgrece,512,mymsgrece.mtype,0);
if(msgrcv_ret==-1)
{
perror("msgrcv error:");
exit(EXIT_FAILURE);
}
if(strcmp(mymsgrece.mtext,"exit")==0) break;
printf("received msg : %s\n",mymsgrece.mtext);
}
msgctl_ret=msgctl(msgid,IPC_RMID,0);
if(msgctl_ret==-1)
{
perror("msgrcv error:");
exit(EXIT_FAILURE);
}
return 0;
}
运行结果: