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)
                     


 
0 0
原创粉丝点击