一、什么是消息队列?

下面的我们来看一下有关消息队列的函数。
二、消息队列的函数
1.消息队列的创建函数
函数头文件 #include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>函数原型 int msgget(key_t key, int msgflg);函数功能 获取System V消息队列标识符函数参数key:由ftok函数合成msgflg:消息队列标志IPC_CREAT 创建标志IPC_EXCL 如果消息队列存在,则报错, errno设置为EEXIST权限控制标志函数返回值 成功:返回消息队列id失败:返回-1,并设置errno
消息队列的创建函数返回的类型是一个int 类型,一个id号,如果要在这个消息队列发送或者接收消息,就需要使用这个id号。当然我们可以创建多个消息队列,因为消息队列的大小是有限的。
在创建这个函数的时候需要先使用fotk()函数,得到一个key_t类型的值,fotk()函数使用来创建一个IPC,包括后面的共享内存、信号量,都需要使用先fotk()函数,不了解的fotk()函数没关系,后面有示例。
2.消息队列的删除
函数头文件 #include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>函数原型 int msgctl(int msqid, int cmd, struct msqid_ds *buf);函数参数msqid:消息队列 idcmd:命令字IPC_STAT:获取消息队列属性IPC_SET:设置消息队列属性IPC_RMID:删除消息队列,用此命名时,第三个参数为NULLbuf:消息队列属性结构体对象指针函数返回值成功:IPC_STAT, IPC_SET, and IPC_RMID 返回0失败:返回-1,并设置 errno
在后面的实例中,仅使用函数去删除消息队列,并没有获取相对应的属性。如果需要获取消息队列的属性,那就需要定义struct msqid_ds结构体,通过结构体来获取属性。
struct msqid_ds结构体的定义:
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of creation or last
modification by msgctl() */
unsigned long msg_cbytes; /* # of bytes in queue */
msgqnum_t msg_qnum; /* # number of messages in queue */
msglen_t msg_qbytes; /* Maximum # of bytes in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */};
再来看一下 定义个 struct ipc_perm是什么类型:
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
可以看到上面的两个结构体中定义的大都是一些时间信息和ID,现在不用太关注这些信息。
3.消息队列的发送和接收
函数头文件 #include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);函数参数:msqid:消息队列IDmsgp:消息结构体指针msgsz:消息内容的长度msgflg:消息队列标志,默认可以填0,IPC_NOWAIT,可以设置非阻塞函数返回值成功:返回0失败:-1,并设置errno
这里参数需要注意的是msgp是结构体指针不是发送数组的指针,这个参数填错会导致进程卡死。
msgsz可以通过sizeof()来获取长度。在后面的实例中msgflg是设置的阻塞,所以这个参数填0。
函数头文件 #include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>函数原型 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);函数参数msqid:消息队列idmsgp:消息结构指针msgsz:最大读取的字节数msgtyp:消息的标识msgflg:消息队列标志,默认可以填0,IPC_NOWAIT,可以设置非阻塞。函数返回值 成功:返回实际读取消息内容的字节数失败:-1,并设置 errno
接收相较于发送只是多了一个参数,是第四个参数msgtyp,这个参数非常关键,它是一个标识,long类型,用来保证接收是正确的消息。这个标识在初始化的时候就已经确定,就好比连连看,只有标识相同才可以接收到对应的消息。
三.消息队列的的使用
下面是消息队列的简单示例:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <error.h>
5 #include <sys/types.h>
6 #include <sys/ipc.h>
7 #include <sys/msg.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10
11 #define PATH "/home/linux/"
12 #define ID 10
13
14 struct mbuf{
15 long mtype; //作为接受消息的标识;
16 char buf[64]; //存给消息的数组;
17 };
18
19 int main()
20 {
21 //定义一个结构体类型的数组;
22 struct mbuf data[4]={0};
23 /*
24 *在结构体数组中有两个的mtype相同;
25 *数组索引0为发送端,1为接受端;
26 *数组索引2为发送端,3为接受端;
27 */
28 data[0].mtype = 1;
29 strcpy(data[0].buf,"hello first process!");
30 data[1].mtype = 1;
31
32 data[2].mtype = 2;
33 strcpy(data[2].buf,"hello sencnd process!");
34 data[3].mtype = 2;
35 key_t ipcs = ftok(PATH,ID); /* 创建ipcs*/
36 if(ipcs == -1){
37 perror("ftok");
38 exit(1);
39 }
40 int id = msgget(ipcs,IPC_CREAT | 0666); /* 创建消息队列*/
41 if(id == -1){
42 perror("msgget");
43 exit(1);
44 }
45 pid_t pid_1 = fork(); /* 创建一个子进程*/
46 if(pid_1 == -1){
47 perror("fork 1");
48 exit(1);
49 }else if(pid_1 == 0){ /* 子进程部分*/
50 sleep(1); /* 延时一秒,确保主进程先执行*/
51 ssize_t rbytes = msgrcv(id,&data[1],sizeof(data[1].buf),data[1].mtype,0);/* 读取消息队列中标识是 1 的消息 */
52 if(rbytes == -1){
53 perror("msgrcv 1");
54 exit(1);
55 }
56 printf("data[1].buf = %s\n",data[1].buf);
57 exit(EXIT_SUCCESS);
58 }else{ /* 主进程部分*/
59 pid_t pid_2 = fork(); /* 创建第二个子进程*/
60 if(pid_2 == -1){
61 perror("fork 2");
62 exit(1);
63 }else if(pid_2 == 0){ /* 第二个子进程部分*/
64 sleep(2);
65 ssize_t rbytes_2 = msgrcv(id,&data[3],sizeof(data[3].buf),data[3].mtype,0);/* 读取标识为 2 的消息*/
66 if(rbytes_2 == -1){
67 perror("msgrcv 2");
68 msgctl(id, IPC_RMID, NULL);
69 exit(1);
70 }
71 printf("data[3].buf = %s\n",data[3].buf);
72 exit(EXIT_SUCCESS);
73 }else{ /* 主进程成部分*/
74 printf("parent start\n");
75 int snd = msgsnd(id,&data[0],sizeof(data[0].buf),0);/* 向消息队列中发送信息*/
76 if(snd == -1){
77 perror("msgsnd 1");
78 msgctl(id, IPC_RMID, NULL);
79 exit(1);
80 }
81 int snd_2 = msgsnd(id,&data[2],sizeof(data[2].buf),0);/* 消息队队列中发送信息*/
82 if(snd == -1){
83 perror("msgsnd 2");
84 msgctl(id, IPC_RMID, NULL);
85 exit(1);
86 }
87 wait(NULL); /* 等待两个子进程结束*/
88 wait(NULL);
89 int ctl = msgctl(id,IPC_RMID,NULL); /* 删除消息队列*/
90 if(ctl == -1){
91 perror("msgctl");
92 msgctl(id, IPC_RMID, NULL);
93 exit(1);
94 }
95 printf("parent end\n");
96 }
97 return 0;
98 }
99 }
运行结果如下:

可以看到,已经成功接收到了数据。
总结
以上的内容就是有关消息队列的内容了,有发现问题的可以提出来,共同进步。
260

被折叠的 条评论
为什么被折叠?



