正文
1.消息队列(queue)
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
版权声明:本文为博主原创文章,未经博主允许不得转载。https://www.cnblogs.com/Dana-gx/p/9724545.html
一、基本概念
IPC:Linux下的进程通信。包括6种:信号(signal)、管道(pipe)和命名管道(FIFO)、消息队列(msq)、共享内存(shm)、信号量、套接字(socke)
POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存
接下来主要讲解的时SYSTEM V消息队列:
消息队列:提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构
二、为什么用消息队列?与管道的区别?
相同:
1> 通信的进程可以是不相关的进程(不同进程)
2> 都是通过发送和接收的方式来传递数据的
3> 对每个数据都有一个最大长度的限制
区别:
1> 避免命名管道的同步和阻塞问题
2> 接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能顺序地接收(msgtype,确定收发数据类型)
3> 消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难
三、消息队列和共享内存查看方法:ipcs
共享内存每列详解:
第一列就是共享内存的key; 第二列是共享内存的编号shmid;
第三列就是创建的用户owner; 第四列就是权限perms;
第五列为创建的大小bytes; 第六列为连接到共享内存的进程数nattach;
第七列是共享内存的状态status。其中显示“dest”表示共享内存段已经被删除,但是还有用户在使用,当该段内存的mode字段设置为SHM_DEST时
就会显示“dest”。当用户调用shmctl的IPC_RMID时,内存先查看多少个进程与这个内存关联着,如果关联数为0,就会销毁这段共享内存,否者设置
这段内存的mod的mode位为SHM_DEST,如果所有进程都不用则删除这段共享内存。
四、消息队列结构模型:
五、system v下的消息队列接口函数:创建、发送、接受和删除
int msgget (key_t key, int msgflg);
msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msg_typ, int msgflg);
msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
int msgctl (int msqid, int cmd, struct msqid_ds *buf);
5.1 msgget函数 用来创建和访问一个消息队列 函数原型如下:
int msgget (key_t key, int msgflg);
key: 某个特定消息队列的键值
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,IPC_CREAT时,key不存在创建,存在忽略。
如果操作成功,msgget将返回一个非负整数,即该消息队列的标识码;如果失败,则返回“-1”
5.2 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 message_type; /* The data you wish to transfer*/
char text[size]; /*the data*/
};
msg_sz:是msg_ptr指向的消息长度,这个长度不能保存消息类型的那个“long int”长整型计算在内
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情。msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误
“0”,如果失败,则返回“-1”
5.3 msgrcv函数 函数原型如下:
int msgrcv(int msgid, void *msg_ptr, size_t msgsz, long int msgtype,int msgflg);
msgid: 由msgget函数返回的消息队列标识码;
msg_ptr:是一个指针,指针指向准备接收的消息;
msgsz:是msg_ptr指向的消息长度,这个长度不能保存消息类型的那个“long int”长整型计算在内;
msgtype:可实现简单的接收优先级;如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。
如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息;
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事;
操作成功,返回接受字符个数,如果失败,则返回“-1”;
5.4 msgctl函数 控制消息队列,与共享内存shmctl相似 函数原型如下:
int msgctl(int msqid, int command, strcut msqid_ds *buf);
msqid: 由msgget函数返回的消息队列标识码
command:是将要采取的动作,(有三个可取值)
如果操作成功,返回“0”;如果失败,则返回“-1”
其中command是将要采取的动作,它可以取3个值,
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为 msgid_ds结构中给出的值
IPC_RMID:删除消息队列
其中buf是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。
struct msgid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
历程代码: 分为四个部分,创建消息队列、不同进程接收发和删除消息队列
1.创建key为0x1000的消息队列: msgget.c文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<stdlib.h>
6
7
8 int main(int argc, char *argv[])
9 {
10 int ret = msgget((key_t)0x1000, IPC_CREAT | IPC_EXCL | 0666);
11 if(ret == -1)
12 {
13 perror("msgget");
14 exit(EXIT_FAILURE);
15 }
16 printf("Message id = %d \n",ret);
17 return 0;
18 }
2.往0x1000消息队列写数据: msgsnd.c文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<string.h>
6 #include<stdlib.h>
7 #include<getopt.h>
8
9 typedef struct msgbuf
10 {
11 long mtype;
12 char mtext[1024];
13 }msgbuf_t;
14 int main(int argc, char* argv[])
15 {
16 int msgid = msgget((key_t)0x1000, 0);
17 if(msgid == -1)
18 {
19 perror("msgget");
20 exit(EXIT_FAILURE);
21 }
22 char c;
23 msgbuf_t msg= {-1,0};
24 while((c = getopt(argc, argv, "t:m:")) != -1)
25 {
26 switch(c)
27 {
28 case 't':
29 msg.mtype = atoi(optarg);
30 break;
31 case 'm':
32 strcpy(msg.mtext, optarg);
33 break;
34 default:
35 fprintf(stderr, "error option! \n");
36 printf("%s -t msgttpe -m message \n",argv[0]);
37 break;
38 }
39 }
40 if(msg.mtype > 0 && msg.mtext[0] != 0)
41 {
42 if(msgsnd(msgid, &msg, strlen(msg.mtext), 0) == -1)
43 {
44 perror("msgsnd");
45 exit(EXIT_FAILURE);
46 }
47 }
48 return 0;
49 }
3.从0x1000消息队列读数据: msgrcv.c文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<string.h>
6 #include<stdlib.h>
7 #include<getopt.h>
8
9 typedef struct msgbuf
10 {
11 long mtype;
12 char mtext[1024];
13 }msgbuf_t;
14
15 int main(int argc, char* argv[])
16 {
17 int msgid = msgget((key_t)0x1000, 0);
18 if(msgid == -1)
19 {
20 perror("msgget");
21 exit(EXIT_FAILURE);
22 }
23
24 char c;
25 msgbuf_t msg= {-1,0};
26 while((c = getopt(argc, argv, "t:m:")) != -1)
27 {
28 switch(c)
29 {
30 case 't':
31 msg.mtype = atoi(optarg);
32 break;
33 default:
34 fprintf(stderr, "error option! \n");
35 printf("%s -t msgttpe -m message \n",argv[0]);
36 break;
37 }
38 }
39 if(msg.mtype > 0 )
40 {
41 if(msgrcv(msgid, &msg, sizeof(msg)-5, msg.mtype, IPC_NOWAIT) == -1)
42 {
43 perror("msgsnd");
44 exit(EXIT_FAILURE);
45 }
46 printf("msgtype = %ld, msg = %s \n",msg.mtype,msg.mtext);
47 }
48 return 0;
49 }
4.删除消息队列: msgctl.c文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<stdlib.h>
6 #include<string.h>
7
8 int main(int argc, char* argv[])
9 {
10 int msgid = msgget((key_t)0x1000, 0);
11 if(msgid == -1)
12 {
13 perror("msgget");
14 exit(EXIT_FAILURE);
15 }
16 if(msgctl(msgid, IPC_RMID, NULL) == -1)
17 {
18 perror("msgctl");
19 exit(EXIT_FAILURE);
20 }
21 return 0;
22 }
Linux下运行过程如下: