linux消息队列
来源:互联网 发布:买到淘宝假货怎么投诉 编辑:程序博客网 时间:2024/06/06 09:28
消息队列是linux中进程间通信的方法之一。消息队列是一个先进先出的队列,对于同一个类型的队列,消息是遵循先进先出的原则的。
消息队列的实现很简单,只有四个函数就解决了,下面介绍下如何实现消息队列。
1 消息队列的创建
函数原型如下:
int msgget (key_t __key, int __msgflg);
第一个参数是一个key,实际上就是一个长整型,通过这个Key唯一标识一个消息队列。这个值可以是我们自己定义一个唯一的整数。也可以通过 ftok来获取一个key。
第二个参数为标识符,IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,与IPC_CREAT一起使用。(用”|”连接),如果消息队列不存在则创建之,否则产生一个错误并返回。
另外还要注意队列的读写权限,例如要支持读写需要这样写IPC_CREAT|0666.
该函数成功返回消息队列的id,失败返回-1。
该函数返回的id唯一标识一个消息队列。msgsnd 、msgrcv、msgctl都通过这个id来操作消息队列。
ftok的函数原型为
key_t ftok( const char * fname, int id );
fname 指定的已经存在的文件名或者目录名(必须确保已经存在的文件名或者目录。且不会被删除)。该文件或者目录不存在将返回-1。
id 可以是自己指定的一个整数。
该函数成功返回产生的key_t的值,失败但会-1;
该函数,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
如果文件被删除再重新创建,那么索引节点号就变了。用ftok创建的key就改变了,那么就会造成进程不能访问同一个队列了。所以要确保该文件不会被删除。
下面是示例代码
int msgid;
key_t key = ftok(".", 20 );
msgid=msgget(key ,IPC_CREAT|0666);
if(msgid<0)
{
printf("create msg error");
}
2 发送消息:
函数原型如下:
int msgsnd (int __msqid, __const void *__msgp, size_t __msgsz, int __msgflg);
__msqid 通过msgget 返回的消息id
__msgp 指向消息缓冲区的指针。linux给出了一个消息的参考结构:
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[1]; /* 消息文本 */
};
对于消息的结构,我们可以不用上面的结构自己定义一个结构。但是第一个参数必须是消息的类型,必须要和参考的结构一样,为一个long型。而且值必须大于等于1 。
mtype后面的成员我们可以任意指定。例如
struct mymsg {
long mtype; /* 消息类型,必须 > 0 */
char mtext[100]; /* 消息文本 */
};
struct mymsg {
long mtype; /* 消息类型,必须 > 0 */
int item1:
char item2;
char itme3[10];
....
};
__msgsz: 发送消息的尺寸,也就是__msgp 指向的数据的尺寸。
__msgflg:为IPC_NOWAIT表示不阻塞,也就是发送的时候如果消息队列已满,将直接返回-1。为0,如果消息队列满将一直阻塞,直到函数可以向队列写数据为止。
该函数成功返回0 失败返回-1。
3 接收消息
函数原型如下:
int msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg);
__msqid :通过msgget 返回的消息id
__msgp :指向接收消息缓冲区的指针。
__msgsz:消息缓冲区的尺寸,也就是__msgp 指向的缓冲区的大小。
__msgtyp :当__msgtyp 为0 时返回整个消息队列中的第一个消息。
当__msgtyp 大于0时返回消息队列中第一个消息类型等于__msgtyp 的消息。消息类型就是前面介绍的消息参考结构中的mtype 。
当__msgtyp 小于0时,返回消息队列中第一个消息类型小于或者等于__msgtyp的绝对值 的第一个消息。
__msgflg:为IPC_NOWAIT表示不阻塞,也就是如果消息队列为空,将直接返回-1并且将错误码设置为ENOMSG。为0,如果消息队列为空,将一直阻塞直到消息队列有数据为止。
该函数成功返回实际读取的数据长度。 失败返回-1。
注意这个函数调用一次只会返回一条消息数据,即使指定__msgsz可以保存多条消息。
4 消息队列的其他操作:
函数原型如下:
int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf);
__msqid 通过msgget 返回的消息id。
__cmd :IPC_STAT 读取消息队列的数据结构msqid_ds,并将其存储在__buf指定的地址中。
IPC_SET 设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自__buf参数。
IPC_RMID 从系统内核中移走消息队列。不论消息队列中是否还有消息,都将溢出消息队列。
该函数返回0 ,执行成功。
返回- 1,执行失败:返回失败是错误码如下:
errno = EACCES (没有读的权限同时cmd 是IPC_STAT )
EFAULT (buf 指向的地址无效)
EIDRM (在读取中队列被删除)
EINVAL (msgqid无效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但调用程序没有写的权限)
注意:因为消息队列是多进程共享的,所以进程结束后系统并不会主动释放消息队列,所以我们在确保消息队列不用的时候要调用msgctl 释放消息队列,以减少不必要的资源占用。
下面我们看看示例代码:
typedef struct
{
long mtype;
char mtext[20];
}my_msg;
int main(int argc, char** argv)
{
int isSend;
int index;
int msgid;
my_msg msg;
if(argc!=3)
{
printf("error para,exit\n");
return 0;
}
isSend = argv[1][0]-0x30;//第一个参数为0表示进程接收消息,非0为表示进程发送消息
index = argv[2][0]-0x30;//第二个参数表示要接收的消息的类型
if(isSend==0)
{
if(index>3||index<0)
{
printf("index error,exit\n");
return 0;
}
}
key_t key = ftok(".", 2 );
printf("key_t = %d\n",key);
msgid=msgget(key ,IPC_CREAT|0666);
if(msgid<0)
{
printf("create msg queue error\n");
return 0;
}
if(isSend==1)
{
struct timespec ts;
while(1)
{
clock_gettime(CLOCK_REALTIME, &ts);
msg.mtype=ts.tv_nsec%2+1;
sprintf(msg.mtext,"msg= %d",msg.mtype);
msgsnd(msgid, &msg, sizeof(my_msg), 0);
printf("send msg=%s\n",msg.mtext);
sleep(3);
}
}
else
{
int len ;
while(1)
{
len = msgrcv(msgid, (void *)&msg, sizeof(my_msg), index, 0);
if(len < 0)
{
printf("get msg error\n" );
}
else
{
printf("process %d get msg =[%s] type=%d\n" ,index ,msg.mtext,msg.mtype);
}
}
}
return 0;
}
例如我们编译出来的可执行文件msgtest
我们分别在终端0运行 msgtest 1 0
在终端1运行 msgtest 0 1
在终端2运行 msgtest 0 2
在终端3运行 msgtest 0 3
我们可以看到在终端0打印 send msg n的时候 (n为1-3)
对应的终端n 就会打印 process n get msg =[msg= n] type=n (n为1-3)
消息队列的实现很简单,只有四个函数就解决了,下面介绍下如何实现消息队列。
1 消息队列的创建
函数原型如下:
int msgget (key_t __key, int __msgflg);
第一个参数是一个key,实际上就是一个长整型,通过这个Key唯一标识一个消息队列。这个值可以是我们自己定义一个唯一的整数。也可以通过 ftok来获取一个key。
第二个参数为标识符,IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,与IPC_CREAT一起使用。(用”|”连接),如果消息队列不存在则创建之,否则产生一个错误并返回。
另外还要注意队列的读写权限,例如要支持读写需要这样写IPC_CREAT|0666.
该函数成功返回消息队列的id,失败返回-1。
该函数返回的id唯一标识一个消息队列。msgsnd 、msgrcv、msgctl都通过这个id来操作消息队列。
ftok的函数原型为
key_t ftok( const char * fname, int id );
fname 指定的已经存在的文件名或者目录名(必须确保已经存在的文件名或者目录。且不会被删除)。该文件或者目录不存在将返回-1。
id 可以是自己指定的一个整数。
该函数成功返回产生的key_t的值,失败但会-1;
该函数,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
如果文件被删除再重新创建,那么索引节点号就变了。用ftok创建的key就改变了,那么就会造成进程不能访问同一个队列了。所以要确保该文件不会被删除。
下面是示例代码
int msgid;
key_t key = ftok(".", 20 );
msgid=msgget(key ,IPC_CREAT|0666);
if(msgid<0)
{
printf("create msg error");
}
2 发送消息:
函数原型如下:
int msgsnd (int __msqid, __const void *__msgp, size_t __msgsz, int __msgflg);
__msqid 通过msgget 返回的消息id
__msgp 指向消息缓冲区的指针。linux给出了一个消息的参考结构:
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[1]; /* 消息文本 */
};
对于消息的结构,我们可以不用上面的结构自己定义一个结构。但是第一个参数必须是消息的类型,必须要和参考的结构一样,为一个long型。而且值必须大于等于1 。
mtype后面的成员我们可以任意指定。例如
struct mymsg {
long mtype; /* 消息类型,必须 > 0 */
char mtext[100]; /* 消息文本 */
};
struct mymsg {
long mtype; /* 消息类型,必须 > 0 */
int item1:
char item2;
char itme3[10];
....
};
__msgsz: 发送消息的尺寸,也就是__msgp 指向的数据的尺寸。
__msgflg:为IPC_NOWAIT表示不阻塞,也就是发送的时候如果消息队列已满,将直接返回-1。为0,如果消息队列满将一直阻塞,直到函数可以向队列写数据为止。
该函数成功返回0 失败返回-1。
3 接收消息
函数原型如下:
int msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg);
__msqid :通过msgget 返回的消息id
__msgp :指向接收消息缓冲区的指针。
__msgsz:消息缓冲区的尺寸,也就是__msgp 指向的缓冲区的大小。
__msgtyp :当__msgtyp 为0 时返回整个消息队列中的第一个消息。
当__msgtyp 大于0时返回消息队列中第一个消息类型等于__msgtyp 的消息。消息类型就是前面介绍的消息参考结构中的mtype 。
当__msgtyp 小于0时,返回消息队列中第一个消息类型小于或者等于__msgtyp的绝对值 的第一个消息。
__msgflg:为IPC_NOWAIT表示不阻塞,也就是如果消息队列为空,将直接返回-1并且将错误码设置为ENOMSG。为0,如果消息队列为空,将一直阻塞直到消息队列有数据为止。
该函数成功返回实际读取的数据长度。 失败返回-1。
注意这个函数调用一次只会返回一条消息数据,即使指定__msgsz可以保存多条消息。
4 消息队列的其他操作:
函数原型如下:
int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf);
__msqid 通过msgget 返回的消息id。
__cmd :IPC_STAT 读取消息队列的数据结构msqid_ds,并将其存储在__buf指定的地址中。
IPC_SET 设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自__buf参数。
IPC_RMID 从系统内核中移走消息队列。不论消息队列中是否还有消息,都将溢出消息队列。
该函数返回0 ,执行成功。
返回- 1,执行失败:返回失败是错误码如下:
errno = EACCES (没有读的权限同时cmd 是IPC_STAT )
EFAULT (buf 指向的地址无效)
EIDRM (在读取中队列被删除)
EINVAL (msgqid无效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但调用程序没有写的权限)
注意:因为消息队列是多进程共享的,所以进程结束后系统并不会主动释放消息队列,所以我们在确保消息队列不用的时候要调用msgctl 释放消息队列,以减少不必要的资源占用。
下面我们看看示例代码:
typedef struct
{
long mtype;
char mtext[20];
}my_msg;
int main(int argc, char** argv)
{
int isSend;
int index;
int msgid;
my_msg msg;
if(argc!=3)
{
printf("error para,exit\n");
return 0;
}
isSend = argv[1][0]-0x30;//第一个参数为0表示进程接收消息,非0为表示进程发送消息
index = argv[2][0]-0x30;//第二个参数表示要接收的消息的类型
if(isSend==0)
{
if(index>3||index<0)
{
printf("index error,exit\n");
return 0;
}
}
key_t key = ftok(".", 2 );
printf("key_t = %d\n",key);
msgid=msgget(key ,IPC_CREAT|0666);
if(msgid<0)
{
printf("create msg queue error\n");
return 0;
}
if(isSend==1)
{
struct timespec ts;
while(1)
{
clock_gettime(CLOCK_REALTIME, &ts);
msg.mtype=ts.tv_nsec%2+1;
sprintf(msg.mtext,"msg= %d",msg.mtype);
msgsnd(msgid, &msg, sizeof(my_msg), 0);
printf("send msg=%s\n",msg.mtext);
sleep(3);
}
}
else
{
int len ;
while(1)
{
len = msgrcv(msgid, (void *)&msg, sizeof(my_msg), index, 0);
if(len < 0)
{
printf("get msg error\n" );
}
else
{
printf("process %d get msg =[%s] type=%d\n" ,index ,msg.mtext,msg.mtype);
}
}
}
return 0;
}
例如我们编译出来的可执行文件msgtest
我们分别在终端0运行 msgtest 1 0
在终端1运行 msgtest 0 1
在终端2运行 msgtest 0 2
在终端3运行 msgtest 0 3
我们可以看到在终端0打印 send msg n的时候 (n为1-3)
对应的终端n 就会打印 process n get msg =[msg= n] type=n (n为1-3)
0 0
- Linux消息队列编程
- linux消息队列
- linux消息队列操作
- linux IPC-消息队列
- linux消息队列函数
- Linux消息队列操作
- linux 消息队列
- linux IPC-消息队列
- Linux的消息队列
- Linux消息队列
- Linux消息队列
- linux消息队列
- linux消息队列
- Linux消息队列
- linux 消息队列 实例
- linux操作系统消息队列
- linux 消息队列 实例
- 自学Linux -- 消息队列
- Spring4.1新特性——Spring MVC增强
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- 编程实现strcpy函数功能(非库函数)
- 沉浸式状态栏源码分析
- 我理解的希尔排序
- linux消息队列
- iOS个人整理29-JSON与xml文件解析
- android如何隐藏头部的标题栏全屏显示
- BZOJ3224普通平衡树
- opencv 图像去噪要点总结
- Spring4.1新特性——Spring缓存框架增强
- 代码开发注释规范
- LeetCode中链表的试题
- 内容自适应 - UILabel & UITableViewCell