POSIX IPC之消息队列

来源:互联网 发布:1hhhhh域名访问升级 编辑:程序博客网 时间:2024/05/01 12:38
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://keren.blog.51cto.com/720558/144933
使用信号和管道传递消息存在一定的限制:信号传递的消息有限,管道虽然可以传输一定量的信息,但是只能传递无格式的字节流。3种新的进程间通信(IPC)机制---消息队列、信号量、共享内存,可以解决这些问题。这些机制最早出现在UNIX中,被编入POSIC:XSI中,Linux支持POSIX标准。
 
(1)关于IPC资源
消息队列、信号量、共享内存都是IPC资源,这些资源在使用之前都需要先创建。Linux内核中,IPC资源都可以使用一个非负整数——IPC标识符(消息队列标识符、信号量标识符、共享内存标识符)来进行标识。
 
在创建一个IPC标识符之前,需要指定一个关键字key,可以调用函数ftok()获得。
int ftok(const char *pathname, int prj_id);//失败返回-1
 
(2)关于消息队列
消息队列实际上就是一个链表(工作就是倒腾链表的,从来没倒腾过这种,o(∩_∩)o...),而消息就是链表中具有特定格式和优先级的记录,进程可以根据一定规则在消息链表中添加消息,需要消息的进程则可以从消息队列中获得所需的信息。
 
消息写入消息队列时,系统会将消息加入到消息队列维护的消息链表中。如图所示:
msg_next------|
msg_type       |------>msg_next------|
msg_ts                       msg_type       |------>msg_next
msg_spot|                  msg_ts
                  | -消息      msg_spot---消息
 
msg_next:下一个消息指针
msg_type:消息类型,用户自己定义
msg_ts:消息长度
msg_spot:消息内容
 
(3)消息队列相关函数
1、创建消息队列:int msgget(key_t key,int msgflag)
该函数用于创建或者访问消息队列,获得消息队列标识符符。key就是上面用ftok()获得的IPC关键字。
 
2、获得/修改消息队列属性:int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msqid为要修改、读取属性的消息队列标识符,
参数cmd如果为IPC_STAT时表示读取内核中记录消息队列信息的数据结构msqid_ds,
返回的消息队列属性存放在buf指向的结构体中。
 
3、消息发送:int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
msqid:要将消息发送的消息队列标识符
msgp:指针指向消息结构体msgbuf
typedef struct {
    long msgtype;  //消息类型,任何大于0的整数,自定义
    char mtext[?]; //消息文本,文本大小由msgsnd中的msgsz决定 
}msgbuf;
msgsz:消息字节数
msgflg:可以为0(忽略该参数)或者IPC_NOWAIT(如果消息队列没有足够空间,如果设置了IPC_NOWAIT,立刻返回;否则函数阻塞,直到等到空间)。
 
4、消息接收:int msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
从msqid代表的消息队列取出消息,存放在msgp指向的缓冲区,缓冲结构定义和发送时使用的msgbuf类似;
msgsz:消息内容长度,msgbuf结构体中mtext的长度;
msgtpye:请求读取的消息类型
msgflg:参数可以取0,也可以取IPC_NOWAIT等。如果为IPC_NOWAIT时,消息队列如果为空,则函数阻塞,直到有消息可以取。
 
(4)实例:
【创建消息队列、发消息】
代码保存systemcall2.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define BUF_SIZE 100
typedef struct {
 long mtype;
 char mtext[BUF_SIZE];
}msg_info;   //用于消息发送
//创建消息队列
int creat_msg_queue()
{
 int proj_id = 1;
 int msg_id;
 key_t key;
 
 key = ftok("/home/gaolu",proj_id);  //创建IPC标识符,需要先获得一个key
 if(-1 == key)
 {
  perror("Can't generate the IPC key.\n");
  return -1;
 }
 msg_id = msgget(key,IPC_CREAT|0660); //如果msg_id不存在,则创建,否则,返回已经存在的队列标识符
 if(-1 == msg_id)
 {
  printf("Can't creat message queue resource.\n");
  return -1;
 }
 
 return msg_id;
}
//向队列发消息
int send_msg(int msg_id,char* message)
{
 int result;
 msg_info MsgInfo;
 
 MsgInfo.mtype = 10;  //随意定义,大于0 && 整数 即可
 strcpy(MsgInfo.mtext,message);
 result = msgsnd(msg_id,&MsgInfo,strlen(message),0);
 if(-1 == result)
 {
  perror("Fail to send message to message queue.\n");
 }
 return result;
}
//显示消息信息|验证消息是否发入队列
int show_msg_queue_stat(int msg_id)
{
 struct msqid_ds MsgQueueInfo;
 int result;
 
 result = msgctl(msg_id,IPC_STAT,&MsgQueueInfo); //获得消息队列信息(所有者,权限,消息数……)
 if(-1 == result)
 {
  perror("Can't get status of message queue.\n");
  return -1;
 } 
 printf("======================Message Queue Info=======================\n");
 printf("Effective user id: %d.\n",MsgQueueInfo.msg_perm.uid);
 printf("Effective user group id: %d.\n",MsgQueueInfo.msg_perm.gid);
 printf("Current numbers of bytes in message queue(non-standard): %ld.\n",MsgQueueInfo.__msg_cbytes);
 printf("Current numbers of message in message queue: %ld.\n",MsgQueueInfo.msg_qnum);
 printf("===============================================================\n");
 return 0;
}
int main(void)
{
 int msg_id; 
 int result;
 msg_id = creat_msg_queue();
 send_msg(msg_id,"Test data..............");
 show_msg_queue_stat(msg_id);
 return 0;
}

【取消息】
代码保存msgrcv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define BUF_SIZE 100
typedef struct {
 long mtype;
 char mtext[BUF_SIZE];
}msg_info;
//读取消息队列中指定类型的消息,将消息内容保存在msg指向的地址空间
int rcv_msg(int msg_id,int msg_type,char* msg)
{
 int result;
 msg_info MsgInfo;
 result = msgrcv(msg_id,&MsgInfo,BUF_SIZE,msg_type,0);
 if(-1 == result)
 {
  perror("Can't receive message from message queue.\n");
  return result;
 }
 strcpy(msg,MsgInfo.mtext);
 return result;
}

int main(int argc,char* argv[])
{
 int result;
 char message[BUF_SIZE];
 int msg_type;
 int msg_id;
 if(3 != argc)
 {
  printf("Useage:%s msgid msg_type.\n",argv[0]);
  return -1;
 }
 msg_type = atoi(argv[2]);
 msg_id = atoi(argv[1]);
//获得指定类型消息并显示消息内容
 result = rcv_msg(msg_id,msg_type,message);
 if(-1 == result)
 {
  perror("Fail to receive message.\n");
 }
 else
 {
  printf("Message: %s.\n",message);
 }
 return result; 
}
【执行结果】
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o msgq systemcall2.c
gaolu@gaolu-desktop:~$ gcc -o rcv msgrcv.c
gaolu@gaolu-desktop:~$ ./msgq
======================Message Queue Info=======================
Effective user id: 1000.
Effective user group id: 1000.
Current numbers of bytes in message queue(non-standard): 23.
Current numbers of message in message queue: 1.
================================================================
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ ipcs -q  //IPC命令 参数q表示消息队列(m:共享内存,s:信号量)
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   
0x01018b15 0          gaolu      777        23           1          
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ ./rcv 0 10        //发消息时消息类型定义为10
Message: Test data...............
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$

本文出自 “淡泊明志,宁静致远” 博客,请务必保留此出处http://keren.blog.51cto.com/720558/144933