进程间通信之消息队列

进程间通信之消息队列

首先,我们来说一下什么是消息队列:
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

一、函数原型

msgget

函数原型:int msgget(key_t, key, int msgflg);

   与其他的IPC机制一样,程序必须提供一个键来命名某个特定的消息队列。当key=0(IPC_PRIVATE)创建一个只能在进程内部通信的消息队列。msgflg是一个权限标志,表示消息队列的访问权限,它与文件的,访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。IPC_CREAT|IPC_EXCL,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错。它返回一个以key命名的消息队列的标识符(非零整数),失败时返回-1,错误信息存于errno中。

错误码:

EACCES:指定的消息队列已存在,但调用进程没有权限访问它

EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志

ENOENT:key指定的消息队列不存在,同时msgflg中没有指定IPC_CREAT标志

ENOMEM:需要建立消息队列,但内存不足

ENOSPC:需要建立消息队列,但已达到系统的限制

msgsnd

函数原型:int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);

msgid:msgget函数返回的消息队列标识符。

msg_ptr:指向准备发送消息的结构体指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定接收的消息来自哪一个类型(频道)的消息(节目),其他字段不限。消息结构定义:

struct my_message{

long int message_type;  //频道(你可以理解为消息通道号)

/* The data you wish to transfer*/  //消息主体

};

msg_sz:msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。

msgflg:用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。

返回值:如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1。msgflg=0,当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列;msgflg=IPC_NOWAIT,当消息队列已满的时候,msgsnd函数不等待立即返回;msgflg=IPC_NOERROR,若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。

错误代码:

EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满

EIDRM:标识符为msgid的消息队列已被删除

EACCESS:无权限写入消息队列

EFAULT:参数msgp指向无效的内存地址

EINTR:队列已满而处于等待情况下被信号中断

EINVAL:无效的参数msgid、msgsz或参数消息类型type小于0

   msgsnd为阻塞函数,当消息队列容量满或消息个数满会阻塞。消息队列已被删除,则返回EIDRM错误;被信号中断返回E_INTR错误。如果设置IPC_NOWAIT消息队列满或个数满时会返回-1,并且置EAGAIN错误。msgsnd解除阻塞的条件有以下三个条件:

① 不满足消息队列满或个数满两个条件,即消息队列中有容纳该消息的空间。

② msgid代表的消息队列被删除。

③ 调用msgsnd函数的进程被信号中断(SIGINT)。

msgrecv

函数原型:int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);

msgid:msgget函数返回的消息队列标识符。

msg_ptr:指向准备接收消息的结构体指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定接收的消息来自哪一个类型(频道)的消息(节目)。消息结构定义:

struct my_message{

long int message_type;  //频道(你可以理解为消息通道号)

/* The data you wish to transfer*/  //消息主体

};

msg_st:msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。

msgtype:msgtype可以实现一种简单的接收优先级,消息是按照接收顺序存于消息队列中,每条消息对应一个消息类型号(频道号)。如果msgtype为0,就获取队列中的第一条消息。如果它的值大于零,将获取具有相同类型号(频道号)的第一条消息。如果它小于零,就获取频道号等于或小于msgtype的绝对值的第一个消息。

msgflg:用于控制当队列中没有相应类型的消息可以接收时将发生的事情。msgflg=0,阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待;msgflg=IPC_NOWAIT,如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG;msgflg=IPC_EXCEPT,与msgtype配合使用返回队列中第一个类型不为msgtype的消息;msgflg=IPC_NOERROR,如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃。

返回值:调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1。

错误代码:

E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR

EIDRM:标识符为msgid的消息队列已被删除

EACCESS:无权限读取该消息队列

EFAULT:参数msgp指向无效的内存地址

ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读

EINTR:等待读取队列内的消息情况下被信号中断

msgrcv解除阻塞的条件有以下三个:

①消息队列中有了满足条件的消息。

②msgid代表的消息队列被删除。

③调用msgrcv()的进程被信号中断(SIGINT)。

msgctl

函数原型:int msgctl(int msgid, int command, struct msgid_ds *buf);

msgid:msgget函数返回的消息队列标识符。

command:是将要采取的动作,它可以取3个值:

IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。

IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值

IPC_RMID:删除消息队列

buf:指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。

struct msqid_ds {//Linux系统中的定义

struct ipc_perm msg_perm; /* Ownership andpermissions*/

time_t msg_stime; /* Time of last msgsnd()*/

time_t msg_rtime; /* Time of last msgrcv()*/

time_t msg_ctime; /* Time of last change */

unsigned long __msg_cbytes; /* Currentnumber of bytes inqueue (non-standard) */

msgqnum_t msg_qnum; /* Current number of messagesinqueue */

msglen_t msg_qbytes; /* Maximum number ofbytesallowed in queue */

pid_t msg_lspid; /* PID of last msgsnd() */

pid_t msg_lrpid; /* PID of last msgrcv() */

};//不同的系统中此结构会有不同的新成员

返回值:成功时返回0,失败时返回-1。

错误代码:

EACCESS:参数cmd为IPC_STAT,无权限读取该消息队列

EFAULT:参数buf指向无效的内存地址

EIDRM:标识符为msgid的消息队列已被删除

EINVAL:无效的参数cmd或msgid

EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行

接下来,我们一步一步来使用这些函数,实现进程间的通信

二、实现

msgget

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 int main()
  9 {
 10     int msgid ;
 11     msgid = msgget(IPC_PRIVATE,0777);
 12     if(msgid < 0)
 13     {
 14         printf("error\n");
 15         return -1;
 16     }
 17     printf("create message queue success msgid = %d\n",msgid);
 18     system("ipcs -q ");
 19     return 0;
 20 }

结果:

create message queue success msgid = 32769

--------- 消息队列 -----------
        msqid      拥有者  权限     已用字节数 消息      
0x00000000 0          wz         777        0            0           
0x00000000 32769      wz         777        0            0   

说明消息队列在内核中创建成功,为32769
msgctl
删除消息队列:

  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 int main()
  9 {
 10     int msgid ;
 11     msgid = msgget(IPC_PRIVATE,0777);
 12     if(msgid < 0)
 13     {
 14         printf("error\n");
 15         return -1;
 16     }
 17     printf("create message queue success msgid = %d\n",msgid);
 18     msgctl(msgid,IPC_RMID,NULL);                                            
 19     system("ipcs -q ");
 20     return 0;
 21 }

结果:

--------- 消息队列 -----------
        msqid      拥有者  权限     已用字节数 消息      
0x00000000 0          wz         777        0            0           
0x00000000 32769      wz         777        0            0  

msgsnd函数

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     msgid = msgget(IPC_PRIVATE,0777);
 17     if(msgid < 0)
 18     {
 19         printf("error\n");
 20         return -1;
 21     }
 22     struct msgbuf sendbuf;
 23     sendbuf.type = 100;
 24 //  printf("please input message:\n");
 25     //fgets(sendbuf.msgtext,128,stdin);
 26     //msgsnd(msgid,(void*)&sendbuf,strlrn(sendbuf.msgtext,0));
 27     printf("please input message:\n");  
 28     printf("create message queue success msgid = %d\n",msgid);
 29     fgets(sendbuf.msgtext,128,stdin);
 30 
 31     msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.msgtext),0);   
 32     while(1);
 33     msgctl(msgid,IPC_RMID,NULL);
 34     system("ipcs -q ");
 35     return 0;  
 36 }

输入:hello

wz@wz-machine:~/linux/msg$ ./a.out 
please input message:
hello
create message queue success msgid = 163843
^C
wz@wz-machine:~/linux/msg$ ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x00000000 0          wz         777        0            0           
0x00000000 32769      wz         777        0            0           
0x00000000 131074     wz         777        1            1           
0x00000000 163843     wz         777        6            1           
0x00000000 196612     wz         777        6            1    

由此可见,消息发送成功,6个字节。接下来,我们来看接收函数。

msgrcv

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     msgid = msgget(IPC_PRIVATE,0777);
 17     if(msgid < 0)
 18     {
 19         printf("error\n");
 20         return -1;
 21     }
 22     struct msgbuf sendbuf, recvbuf;
 23     sendbuf.type = 100;
 24     printf("please input message:\n");  
 25     printf("create message queue success msgid = %d\n",msgid);
 26     fgets(sendbuf.msgtext,128,stdin);
 27 
 28     msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.msgtext),0);   
 29     msgrcv(msgid,(void*)&recvbuf,128,100,0);
 30     printf("recvbuf:%s\n",recvbuf.msgtext);
 31     msgctl(msgid,IPC_RMID,NULL);
 32     system("ipcs -q ");
 33     return 0;
 34 }
 35                              

结果:

wz@wz-machine:~/linux/msg$ gcc msg.c 
wz@wz-machine:~/linux/msg$ ./a.out 
please input message:
create message queue success msgid = 229381
hello
recvbuf:hello


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x00000000 0          wz         777        0            0           
0x00000000 32769      wz         777        0            0           
0x00000000 131074     wz         777        1            1           
0x00000000 163843     wz         777        6            1           
0x00000000 196612     wz         777        6            1

但是我们会发现,读完数据之后,消息队列会自动删除,所以只能读一次。
写和读的单向发送:
write:

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     int key;
 17     key = ftok("./a.c",'a');
 18     if(key<0)
 19     {
 20         printf("error\n");
 21         return -1;
 22     }
 23     msgid = msgget(key,IPC_CREAT|0777);
 24     if(msgid < 0)
 25     {   
 26         printf("msg error\n");
 27         return -1;
 28     }
 29     struct msgbuf sendbuf, recvbuf;
 30     sendbuf.type = 100;
 31     printf("please input message:\n");  
 32     printf("create message queue success msgid = %d\n",msgid);
 33     while(1)
 34     {
 35         fgets(sendbuf.msgtext,128,stdin);
 36 
 37         msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.msgtext),0);   
 38     //  msgrcv(msgid,(void*)&recvbuf,128,100,0);
 39     //  printf("recvbuf:%s\n",recvbuf.msgtext);
 40     //  msgctl(msgid,IPC_RMID,NULL);
 41     //  system("ipcs -q ");
 42     }
 43     return 0;  
 44 }      

read:

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     int key;
 17     key = ftok("./a.c",'a');
 18     msgid = msgget(key,IPC_CREAT|0777);
19     if(msgid < 0)
 20     {
 21         printf("error\n");
 22         return -1;
 23     }
 24     struct msgbuf sendbuf, recvbuf;
 25     printf("create message queue success msgid = %d\n",msgid);
 26     while(1)
 27     {
 28         memset(recvbuf.msgtext,0,128);
 29         msgrcv(msgid,(void*)&recvbuf,128,100,0);
 30         printf("recvbuf:%s\n",recvbuf.msgtext);
 31     //  msgctl(msgid,IPC_RMID,NULL);
 32     //  system("ipcs -q ");
 33     }
 34     return 0;
 35 }

写端写入:

wz@wz-machine:~/linux/msg$ ./write 
please input message:
create message queue success msgid = 393225
hello
world

读端读出:

wz@wz-machine:~/linux/msg$ ./read 
create message queue success msgid = 393225
recvbuf:hello

recvbuf:world

服务器端和客户端之间的通信:
ser.c

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     int key;
 17     struct msgbuf sendbuf,recvbuf;
 18     key = ftok("./a.c",'a');
19     if(key<0)
 20     {
 21         printf("error\n");
 22         return -1;
 23     }
 24     msgid = msgget(key,IPC_CREAT|0777);
 25     if(msgid < 0)
 26     {
 27         printf("msg error\n");
 28         return -1;
 29     }
 30     printf("create message queue success msgid = %d\n",msgid);
 31     pid_t pid;
 32     pid = fork();
 33     if(pid > 0 )
 34     {
35         sendbuf.type = 100;
 36         while(1)
 37         {
 38             memset(sendbuf.msgtext,0,128);
 39             printf("please input messgae:\n");
 40 
 41             fgets(sendbuf.msgtext,128,stdin);
 42 
 43             msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.msgtext),0);   
 44         //  msgrcv(msgid,(void*)&recvbuf,128,100,0);
 45         //  printf("recvbuf:%s\n",recvbuf.msgtext);
 46         //  msgctl(msgid,IPC_RMID,NULL);
 47         //  system("ipcs -q ");
 48         }
 49     }
 50         if(pid == 0)
51         {
 52             while(1)
 53             {
 54                 memset(recvbuf.msgtext,0,128);
 55                 msgrcv(msgid,(void*)&recvbuf,128,200,0);
 56                 printf("recvbuf:%s\n",recvbuf.msgtext);
 57             }
 58         }
 59         msgctl(msgid,IPC_RMID,NULL);
 60         system("ipcs -q ");
 61         return 0;
 62 }

cli.c

 1 #include<stdio.h>                                                           
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include <sys/msg.h>
  5 #include <sys/ipc.h>
  6 #include <sys/types.h>
  7 
  8 struct msgbuf
  9 {
 10     long type;
 11     char msgtext[128];
 12 };
 13 int main()
 14 {
 15     int msgid ;
 16     int key;
 17     struct msgbuf sendbuf,recvbuf;
 18     key = ftok("./a.c",'a');
  19     if(key<0)
 20     {
 21         printf("error\n");
 22         return -1;
 23     }
 24     msgid = msgget(key,IPC_CREAT|0777);
 25     if(msgid < 0)
 26     {
 27         printf("msg error\n");
 28         return -1;
 29     }
 30     printf("create message queue success msgid = %d\n",msgid);
 31     pid_t pid;
 32     pid = fork();
 33     if(pid == 0 )   
 34     {
 35         sendbuf.type = 200;
 36         while(1)
 37         {
 38             memset(sendbuf.msgtext,0,128);
 39             printf("please input messgae:\n");
 40 
 41             fgets(sendbuf.msgtext,128,stdin);
 42 
 43             msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.msgtext),0);   
 44         //  msgrcv(msgid,(void*)&recvbuf,128,100,0);
 45         //  printf("recvbuf:%s\n",recvbuf.msgtext);
 46         //  msgctl(msgid,IPC_RMID,NULL);
 47         //  system("ipcs -q ");
 48         }
49         }
 50         if(pid >  0)
 51         {
 52             while(1)
 53             {
 54                 memset(recvbuf.msgtext,0,128);
 55                 msgrcv(msgid,(void*)&recvbuf,128,100,0);
 56                 printf("recvbuf:%s\n",recvbuf.msgtext);
 57             }
 58         }
 59 
 60     
 61     msgctl(msgid,IPC_RMID,NULL);
 62     system("ipcs -q ");
 63 
 64         return 0;
 65 }

结果:

wz@wz-machine:~/linux/msg$ ./ser 
create message queue success msgid = 557058
please input messgae:
hello
please input messgae:
recvbuf:world

cli:
wz@wz-machine:~/linux/msg$ ./cli 
create message queue success msgid = 557058
please input messgae:
recvbuf:hello

world
please input messgae:

请多多指教!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值