键值构建ftok()函数
ftok()函数将路径名和项目的表示符转变为一个系统V的IPC键值,其原型如下:
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char* pathname , int proj_id);
其中pathname必须是一件存在的目录,而proj_id则是一个8位的值,通常用a,b等表示。例如建立如下目录后:
mkdir -p /ipc/msg
用如下代码生成一个键值:
...
key_t key;
char* msgpath = “/ipc/msg/”;
key = ftok(msgpath,’a’);
if(key != -1)
{
printf(“成功建立KEY\n”);
}
else
{
printf(“建立KEY失败\n”);
}
...
获得消息msgget()函数
创建一个新的消息队列,或者访问一个现有的队列,可以使用函数msgget(),其原型如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key , int msgflg);
msgget()函数的第一个参数是键值,可以用ftok()函数生成,这个关键字的值将被拿来与内核中其他消息队列的现有的关键字值相比较,打开或者访问操作依赖于msgflg参数的内容。
(1)IPC_CREAT:如果在内核中不存在该队列,则创建它。
(2)IPC_EXCL:当与IPC_CREAT一起使用时,如果队列早已存在则将出错。
如果只使用了IPC_CREAT,msgget()函数或者返回新创建消息队列的消息队列标识符,或者会返回现有的具有同一关键字值的队列的标识符。如果同时使用了IPC_EXCL和IPC_CREAT,那么将可能会有两个结果:或者创建一个新的队列,或者如果该队列存在,则调用将出错,并返回-1。IPC_EXCL本身是没有什么用处的,但在与IPC_CREAT组合使用时,它可以用于保证没有一个现存的队列为了访问而被打开。例如,下面的代码创建一个消息队列:
...
ket_t key;
int msg_flags,msg_id;
msg_flags = IPC_CREAT | IPC_EXCL;
msg_id = msgget(key,msg_flags|0x0666);
if(-1 == msg_id)
{
printf(“消息建立失败\n”);
return 0;
}
...
发送消息msgsnd()函数
一旦获得了队列标识符,用户就可以开始在该消息队列上执行相关操作了。为了向队列传递消息,用户可以使用msgsnd()函数:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg);
总共有4个参数,首先要有一个msqid即消息队列的id,通过msgget(key, 0)可以得到;第二个参数便是我们定义的结构体,要将地址传递过去;第三个参数是发送数据的大小,即struct msgbuf结构体中mtext的大小,不包括数据的类型;最后一个参数并不重要,我们可以不必了解,在使用时一律填0即可。
我们在发送数据时要将数据的类型填入mtype,将数据填入mtext,然后这个结构体作为参数传入消息队列,这样便可以实现数据的优先级接受。
msgsnd()函数的第一个参数是队列标识符,它是前面调用msgget()获得的返回值。第二个参数是msgp,它是一个void类型的指针,指向一个消息缓冲区。msgsz参数则包含着消息的大小,它是以字节为单位的,其中不包括消息类型的长度(4个字节长)。
msgflg参数可以设置为0(表示忽略),也可以设置为IPC_NOWAIT。如果消息队列已满,则消息即将不会被写入到队列中。如果没有指定IPC_NOWAIT,则调用进程将被中断(阻塞),直到可以写消息为止,例如,如下代码向已经打开的消息队列发送消息。
...
struct msgmbuf{
int mtype;
char mtext[10];
};
int msg_sflags;
int msg_id;
struct msgmbuf msg_mbuf;
msg_sflags = IPC_NOWAIT;
msg_mbuf.mtype = 10;
memcpy(msg_mbuf,mtext,“测试消息”,sizeof(“测试消息”));
ret = msgsnd(msg_id,&msg_mbuf,sizeof(“测试消息”),msg_sflags);
if(-1 == ret)
{
printf(“发送消息失败\n”);
}
...
首先将要发送的消息打包进msg_msgbuf。mtext域中,然后调用msgsnd发送消息给内核。这里的mtype设置了类型为10,当接收时必须设置此域为10,才能接收到这时发送的消息。msgsnd函数的msg_id是之前msgget创建的。
接收消息msgrcv()函数
当获得队列标识符后,用户就可以开始在该消息队列上执行消息队列的接收操作。msgrcv()函数用于接收队列标识符中的消息,函数原型如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
ssoze_t msgrcv(int msqid,void* msgp,size_t msgsz,long msgtyp,int msgflg);
msgrcv()函数有5个参数,比msgsnd()函数多了一个参数,即第4个参数数据类型,因为发送消息时,数据的类型在结构体中保存着,因此不需要再次传入,可是在读消息时我们需要读取特定类型的消息,因此我们要将指定的数据类型作为参数传入。以满足我们的优先级读取。当msgtyp==0时即按照先进先出的顺序读取当前消息队列中第一个消息。
(1)msgrcv()函数的第一个参数msgid是用来指定的,在消息获取过程中所使用的队列(该值是由前面调用msgget()得到的返回值)。
(2)第二个参数msgp代表消息缓冲区变量的地址,获取的消息将存放在这里。
(3)第三个参数msgsz代表缓冲区结构的大小,不包括mtype成员的长度。
(4)第四个参数msgtyp指定要从队列中获取的消息类型。内核将查找队列中具有匹配类型的第一个到达的消息,并把它复制返回到由msgp参数所指定的地址中。如果msgtyp参数传递一个为0的值,则将返回队列中最老的消息,不管该消息的类型是什么。
(5)第五个参数msgflg:
IPC_NOWAIT:如果没有返回条件的消息调用就立即返回,此时错误码为ENOMSG
IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃。
如果把IPC_NOWAIT作为一个标志传递给该函数,而队列中没有任何消息。则该次调用将会向调用进程返回ENOMSG。否则,调用进程将阻塞,直到满足msgrcv()参数的消息到达队列为止。如果在客户等待消息的时候队列被删除了,则返回EIDRM。如果在进程阻塞并等待消息的到来时捕获到一个信号,则返回EINTR。函数msgrcv的使用代码如下:
msg_rflags = IPC_NOWAIT|MSG_NOERROR
ret = msgrcv(msg_id, &msg_mbuf,10,10,msg_rflags);
if(-1 == ret)
{
printf(“接收消息失败\n”);
}
else
{
printf(“接收消息成功,长度:%d\n”,ret);
}
上面的代码中将mtype设置为10,可以获得之前发送的内核的消息蝴蝶(因为之前发送的mtype值也设置为10),megrcv返回值为接收到的消息长度。
消息控制msgctl()函数
通过前面的介绍以及知道如何在应用程序中简单地创建和利用消息队列,下面介绍一下如何直接地对那些与特点的消息队列相联系的内部结构进行操作,为了在一个消息队列上执行控制操作,用户可以使用msgctl()函数
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds* buf);
msgctl()函数向内核发送一个cmd命令,内核根据此来判断进行何种操作,buf为应用层和内核空间进行数据交换的指针。其中cmd可以为如下值:
(1)IPC_STAT:获取队列的msqid_ds结构,并把它存放在buf变量所指定的地址中,通过这种方式,应用层可以获得当前消息队列的设置情况。例如是否有消息到来,消息队列的缓冲区设置等。
(2)IPC_SET:设置队列的msqid_ds结构的ipc_perm成员你值,它是从buf中取得该值的。通过IPC_SET命令,应用层可以设置消息队列的状态,例如修改消息队列的权限,使其他用户可以访问或者不能访问当前的队列,甚至可以设置消息队列的某些当前值来伪装。
(3)IPC_RMID:内核删除队列。使用此命令执行后,内核会把此消息队列从系统中删除。
消息队列的一个例子
在建立消息队列后,打印其属性,并在每次发送和接收后均查看其属性,最后对消息队列进行修改。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/types.h>
4 #include<sys/ipc.h>
5 #include<unistd.h>
6 #include<sys/msg.h>
7 #include<string.h>
8
9 void msg_show_attr(int msg_id,struct msqid_ds msg_info)
10 {
11 int ret = -1;
12 sleep(1);
13 ret = msgctl(msg_id,IPC_STAT,&msg_info);
14
15 if(-1 == ret)
16 {
17 printf("获得消息信息失败\n");
18 return ;
19 }
20
21 printf("\n");
22 printf("现在队列中的字节数:%d\n",msg_info.msg_cbytes);
23 printf("队列中消息数:%d\n",msg_info.msg_qnum);
24 printf("队列中最大字节数:%d\n",msg_info.msg_qbytes);
25 printf("最后发送消息的进程pid:%d\n",msg_info.msg_lspid);
26 printf("最后接收消息的进程pid:%d\n",msg_info.msg_lrpid);
27 printf("最后发送消息的时间:%s\n",ctime(&(msg_info.msg_stime)));
28 printf("最后接收消息的时间:%s\n",ctime(&(msg_info.msg_rtime)));
29 printf("最后变化时间:%s\n",ctime(&(msg_info.msg_ctime)));
30 printf("消息UID是:%d\n",msg_info.msg_perm.uid);
31 printf("消息GID是:%d\n",msg_info.msg_perm.gid);
32 }
33
34 int main(void)
35 {
36 int ret = -1;
37 int msg_flags,msg_id;
38 key_t key;
39 struct msgmbuf{
40
41 int mtype;
42 char mtext[10];
43 };
44
45 struct msqid_ds msg_info;
46 struct msgmbuf msg_mbuf;
47
48 int msg_sflags,msg_rflags;
49 char* msgpath = "/ipc/msg/";
50 key = ftok(msgpath,'b');
51
52 if(key != -1)
53 {
54 printf("成功建立KEY\n");
55 }
56 else
57 {
58 printf("建立KEY失败\n");
59 }
60
61 msg_flags = IPC_CREAT|IPC_EXCL;
62 msg_id = msgget(key,msg_flags|0x0666);
63
64 if(-1 == msg_id)
65 {
66 printf("消息建立失败\n");
67 return 0;
68 }
69
70 msg_show_attr(msg_id,msg_info);
71
72 msg_sflags = IPC_NOWAIT;
73 msg_mbuf.mtype = 10;
74 memcpy(msg_mbuf.mtext,"测试消息",sizeof("测试消息"));
75 ret = msgsnd(msg_id,&msg_mbuf,sizeof("测试消息"),msg_sflags);
76
77 if(-1 == ret)
78 {
79 printf("发送消息失败\n");
80 }
81 msg_show_attr(msg_id,msg_info);
82
83 msg_rflags = IPC_NOWAIT|MSG_NOERROR;
84 ret = msgrcv(msg_id,&msg_mbuf,10,10,msg_rflags);
85 if(-1 == ret)
86 {
87 printf("接收消息失败\n");
88 }
89 else
90 {
91 printf("接收消息成功,长度:%d\n",ret);
92 }
93 msg_show_attr(msg_id,msg_info);
94
95 msg_info.msg_perm.uid = 8;
96 msg_info.msg_perm.gid = 8;
97 msg_info.msg_qbytes = 12345;
98 ret = msgctl(msg_id,IPC_SET,&msg_info);
99
100 if(-1 == ret)
101 {
102 printf("设置消息属性失败\n");
103 return 0;
104 }
105 msg_show_attr(msg_id,msg_info);
106
107 ret = msgctl(msg_id,IPC_RMID,NULL);
108 if(-1 == ret)
109 {
110 printf("删除消息失败\n");
111 return 0;
112 }
113 return 0;
114 }
显示消息属性的函数msg_show_attr()
msg_show_attr()函数根据用户输入的消息ID,将消息的属性打印出来。函数根据输入参数msg_id获得消息的信息,将消息队列中的字节数,消息数,最大字节数,最后发送消息的进程,最后接收消息的进程,最后发送消息的时间,最后接收消息的时间,最后消息变化的时间及消息的UID和GID等信息进行打印。
主函数main()
主函数先用函数ftork()使用路径“/ipc/msg.h”获得一个键值,之后进行相关的操作并打印消息的属性。
(1)调用函数msgget()获得一个消息后,打印消息的属性
(2)调用函数msgsnd()发送一个消息后,打印消息的属性
(3)调用函数msgrcv()接受一个消息后,打印消息的属性
(4)最后,调用函数msgctl()并发送命令IPC_RMID销毁消息队列