Linux进程间的通信方式-———消息队列。
消息队列和共享内存类似
消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息。
消息队列存在于系统内核中,消息的数量受系统限制。
我们来看一下有关消息队列的函数。
msgget();msgsnd();msgrcv();msgctl();
第一个函数:
#include
intmsgget(key_tkey,intmsgflg);
功能:创建一个消息队列或取得一个已经存在的消息队列。
返回值:成功返回消息队列的标示符(ID),失败为-1;
参数:
_key:
1.消息队列的键值,为IPC_PRIVATE意思就是创建一个只能被创建进程读写的消息队列。
2.不是IPC_PRIVATE.则我们可以指定一个值6666,还可以用ftok();函数来获得一个唯一的键值。(ftok()函数见其它文章);
_msgflg:创建消息队列的创建方式或权限。
创建方式有:IPC_CREAT如果内存不存在则创建一个消息队列,否则取得。
IPC_EXCL只有不消息队列存在的时候,新的消息队列才会被创建,否则会产生错误。
Ok,看个例子:
#include
#include
#include
#defineMYMSG_KEY6666
intmain(intargc,charargv[])
{
intmsgid;
msgid=msgget(ftok(".",100),IPC_CREAT|0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
return0;
}
运行结果如下:
我们可以用ipcs-q来查看是否创建成功。
用ipcrm–qmsgid号
==========================================================================
第二个函数:
#include
Intmsgsnd(intmsgid,structmsgbufmsgp,intmsgsz,intmsgflg);
功能:往队列中发送一个消息。
返回值:成功返回0,失败返回-1;
参数:
msgid消息表识id也就是msgget()函数的返回值。
msgp:指向消息缓冲区的指针,该结构体为
structmymesg{
longmtype;/positivemessagetype消息的类型/
charmtext[512];/messagedata,oflengthnbytes存放消息数据内容/
};
msgsz,:是消息的字节大小,不包含消息类型的长度(4个字节)
msgflg:可以设置为0;或者使用IPC_NOWAIT,如果消息队列已经满了,那么次消息就不会写到消息队列中,控制将返回到对于进程中。如果没有指明,调用进程会被挂起,直到消息可以写到消息队列中。
看一个一个例子:
#include
#include
#include
#include
#defineMYMSG_KEY6666
structmymesg{
longmtype;/positivemessagetype/
charmtext[512];/messagedata,oflengthnbytes/
};
intmain(intargc,charargv[])
{
intmsgid,msgsend_ret;
charbuf[25];
structmymesgmsg_send;
msgid=msgget(ftok(".",100),IPC_CREAT|0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
//writemessagestomsgqueue
msg_send.mtype=1;
printf("enteramessage:\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("msggeterror:");
exit(EXIT_FAILURE);
}
return0;
}
运行结果:在此我们写入2个消息
我们可以看到消息的数量为2
==================================================================
既然我们向消息队列中写入了那么我们就可以读取消息队列中的消息了。
那就要用到msgrcv();
格式:#include
intmsgrcv(intmsgid,structmsgbufmsgp,intmsgsz,longmtype,intmsgflg);
功能:读取消息,从消息队列中读走消息,是读走不是读取,也就是读完之后没有了,这种机制类似于管道。
返回值:成功返回0,失败返回-1;
参数:
msgid:消息队列的id号
msgp:是我们要读到的消息数据存储消息的地址。
msgsz是消息的长度不包含mtype的长度,我们可以这样算:
msgsz=sizeof(structmsgbuf)-sizeof(long);
mtype是我们要从消息队列中读取的消息的类型。如果此值为0,则会读取时间最长的那一条消息,不论是什么类型,也就是我们第一个写入消息队列中的消息。
msgflg可以设置为0,代表该进程将一直阻塞,直到有消息可读停止。但我们可以吧该值设为IPC_NOWAIT表示,如果没有消息可读时,则立刻返回-1,进程被挂起。
看例子:
我们把刚才写的2个消息在读出来。
#include
#include
#include
#include
#defineMYMSG_KEY6666
structmymesg{
longmtype;/positivemessagetype/
charmtext[512];/messagedata,oflengthnbytes/
};
intmain(intargc,charargv[])
{
intmsgid,msgsend_ret;
charbuf[25];
structmymesgmsg_send;
msgid=msgget(ftok(".",100),IPC_CREAT|0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
//writemessagestomsgqueue
/0msg_send.mtype=1;
printf("enteramessage:\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("msggeterror:");
exit(EXIT_FAILURE);
}
/
//readmseeagsfrommsgqueue
intmsgrcv_ret;
structmymesgmymsgrece;
msgrcv_ret=msgrcv(msgid,&mymsgrece,sizeof(structmymesg)-sizeof(long),1,0);
//读取消息的类型为1,长度为sizeof(structmymesg)-sizeof(long)把读到
//的消息放到mymsgrece这这结构体中。
if(msgrcv_ret==-1)
{
perror("msgrcverror:");
exit(EXIT_FAILURE);
}
printf("receivedmsgfromqueue:%s\n",mymsgrece.mtext);
return0;
}
运行结果:我们执行两次,把两个刚才写的两个消息都读出来。
从结果中我们看得出:
1.先写进去的先读出来,即遵循先进先出的原则。
2.当我们尝试再去读时,它会阻塞。
3.用ipcs-q查看她真是遵循读走就没有了的原则。
==========================================================
第四个函数,既消息队列进行处理函数,比如删除消息队列。
还可以进行获取消息队列的详细信息,改变消息队列的信息等。
格式:
#include
intmsgctl(intmsgid,intcmd,structmsgqid_dsbuf);
功能:对消息队列的控制处理函数。
返回值:成功返回0,失败返回-1.
参数:
msgid消息队列的ID,也就是msgget()函数的返回值。
cmd命令,对消息队列的处理
IPC_RMID从系统内核中删除消息队列,相当于我们在终端输入ipcrm-q消息队列id
IPC_STAT获取消息队列的详细消息。包含权限,各种时间,id等
IPC_SET设置消息队列的信息。
buf:存放消息队列状态的结构体的地址。
看个例子:
#include
#include
#include
#include
#defineMYMSG_KEY6666
intmain(intargc,charargv[])
{
intmsgid,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("msggeterror:");
exit(EXIT_FAILURE);
}
//deletequeue
/msgctl_ret=msgctl(msgid,IPC_RMID,0);
if(msgctl_ret==-1)
{
perror("msgctlerror:");
exit(EXIT_FAILURE);
}
printf("deletedqueue%dok.\n",msgid);
/
return0;
}
我们先把msgct函数注释,创建出一个消息队列,然后把创建的注释,启用msgct函数。
运行结果如下:
我们把第二个参数改成IPC_STAT获取消息队列的详细消息。包含权限,各种时间,id等
例子:
#include
#include
#include
#include
#defineMYMSG_KEY6666
structmymesg{
longmtype;/positivemessagetype/
charmtext[512];/messagedata,oflengthnbytes/
};
voidmsg_stat(intmsgid,structmsqid_dsmsg_info)
{
intret;
ret=msgctl(msgid,IPC_STAT,&msg_info);
if(ret==-1)
{
perror("msgctlerror:");
exit(EXIT_FAILURE);
}
printf("\n");
printf("currentnumberofbytesonqueueis%d\n",msg_info.msg_cbytes);
printf("maxofbytesonqueueid%d\n",msg_info.msg_qbytes);
printf("numberofmessagesinqueueis%d\n",msg_info.msg_qnum);
printf("lastchangetimeis%s\n",ctime(&msg_info.msg_ctime));
printf("messageuidis%d\n",msg_info.msg_perm.uid);
printf("messagegidis%d\n",msg_info.msg_perm.gid);
}
intmain(intargc,charargv[])
{
charbuf[25];
intmsgid,msgsend_ret,msgctl_ret;
structmymesgmsg_send;
msgid=msgget(ftok(".",100),IPC_CREAT|0644);
//msgid=msg(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
//writemessagestomsgqueue
msg_send.mtype=1;
printf("enteramessage:\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("msggeterror:");
exit(EXIT_FAILURE);
}
structmsqid_dsmsg_ginfo;
msg_stat(msgid,msg_ginfo);
return0;
}
运行结果:
Ok.我们综合用一下这4个函数
msgget()类似文件操作函数open();
msgsnd();类似文件操作函数write();
msgrcv();类似文件操作函数read();
msgctl();类似文件操作函数close();
模拟银行的取号系统:
源程序1(取号机);
#include
#include
#include
#include
#defineMYMSG_KEY6666
structmymesg{
longmtype;/positivemessagetype/
charmtext[512];/messagedata,oflengthnbytes/
};
intmain()
{
intmsgid,msgsend;
structmymesgmymsgsend;
msgid=msgget(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
mymsgsend.mtype=1;
while(1)
{
printf("enteratypeandamsgusetosend:\n");
scanf("%d%s",&mymsgsend.mtype,mymsgsend.mtext);
msgsend=msgsnd(msgid,&mymsgsend,strlen(mymsgsend.mtext)+1,0);
if(msgsend==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
if(strcmp(mymsgsend.mtext,"exit")==0)break;
}
return0;
}
源程序2(综合业务):
#include
#include
#include
#defineMYMSG_KEY6666
structmymesg{
longmtype;/positivemessagetype/
charmtext[512];/messagedata,oflengthnbytes/
};
intmain()
{
intmsgid,msgrcv_ret,msgctl_ret;
structmymesgmymsgrece;
msgid=msgget(MYMSG_KEY,IPC_CREAT|0644);
printf("msgid=%d\n",msgid);
if(msgid==-1)
{
perror("msggeterror:");
exit(EXIT_FAILURE);
}
while(1)
{
scanf("%d",&mymsgrece.mtype);
msgrcv_ret=msgrcv(msgid,&mymsgrece,512,mymsgrece.mtype,0);
if(msgrcv_ret==-1)
{
perror("msgrcverror:");
exit(EXIT_FAILURE);
}
if(strcmp(mymsgrece.mtext,"exit")==0)break;
printf("receivedmsg:%s\n",mymsgrece.mtext);
}
msgctl_ret=msgctl(msgid,IPC_RMID,0);
if(msgctl_ret==-1)
{
perror("msgrcverror:");
exit(EXIT_FAILURE);
}
return0;
}
运行结果:
|
|