Linux IPC 之消息队列

认识消息队列

消息队列提供了从一个进程到另一个进程发送一个数据块(整发整收)的能力。

每个数据块被认为有一个应用类型, 接收者进程接受的数据块可以有不同的类型。

消息队列的每一个发送和接收的数据块是有最大字节限制的(MSGMAX), 整个消息队列的大小也是有字节限制的(MSGMNB), 系统中的消息队列数量也有最大限制(MSGMNI)。

与消息队列相关的系统调用

1.

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 int msgget(key_t key, int msgfg);

key代表要创建的消息队列的键
msgflag通常是通过IPC_CREAT或者IPC_EXCL来设置的
IPC_CREAT|IPC_EXCL代表创建消息队列,如果这个消息队列已经存在,则立即出错返回
IPC_CREAT则代表创建消息队列,不存在则创建,存在则直接打开
如果成功返回非负消息队列标识符。

msgget函数返回的是进程在调用发收消息函数是需要使用的消息队列标识。 但是为了使多个进程可以在同一个IPC对象上汇聚。 所以要有IPC对象的外部名key。
ftok() 两个参数的目的是为了 生成表示唯一一个IP C对象的 的IPC key 两个参数的二元组要唯一

#include<sys/ipc.h>
key_t ftok(const char* path, int id);

path和id表示通信的进程共同认同的路径名和项目id。

2.

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgctl函数对消息队列执行多种操作。
msgid是消息队列的标识符。
cmd参数是对msgid队列要执行的操作

cmd:
IPC_RMID 从系统中删除该消息队列以及消息队列中的数据。
IPC_STAT 取出此队列的msqid_ds 结构 放在buf指向的结构体中.
IPC_SET 在进程权限允许的情况下将buf指向的结构体中的部分信息复制到与这个队也有关的msgid_ds中。

3.
int msgsnd(int msgid, const void* msgp, size_t msgsz, long int msgtyp, int msgflg)

msgid 消息队列标识。
msgp指向要发的数据块(一个包含表示类型的long int 和char text[msgsz], )
msgsz 表示要发送的数据块结构体中数据的大小字节数。
msgtyp 表示所发送消息的类型, 它可以实现接收优先级的简单形式。
msgflg 用来表示当消息队列被写满时会发生什么。

msgflg == IPC_NOWAIT 时 表示队列满不等待 返回EAGIN错误。
msgflg == MSG_NOERROR 时 大于消息队列要求的数据被截断。
msgtyp ==0 时返回第一个数据 < 0 时返回第一个不大于概数绝对值类型大小的数字, > 0时返回队列里第一条等于msgtyp的数据块(结构体)。

发送消息的数据块(结构体) 需要自定义

        struct msgbuf {
                long mtype;       /* message type, must be > 0 */类型必须大于0
               char mtext[1];    /* message data */块大小
           };

4.
int msgrcv(int msgid, const void* msgp, size_t msgsz, long int msgtyp, int msgflg);

IPC 对象的数据结构

内核中每一个IPC对象都有一个ipc_prem结构体。

ipc_perm 结构定义于中,原型如下:
struct ipc_perm
{
key_t        key;                        调用shmget()时给出的关键字
uid_t           uid;                      /*所有者的有效用户ID */
gid_t          gid;                       /*所有者所属组的有效组ID*/ 
uid_t          cuid;                    /* 创建 者的有效用户ID*/
gid_t         cgid;                   /*  创建者所属组的有效组ID*/
unsigned short   mode;    /* Permissions + SHM_DEST和SHM_LOCKED标志*/
unsignedshort    seq;          /* 序列号*/
}; 
构构消息队列的数据结构
struct msqid_ds
  {
    struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;      /* first message on queue,unused  */
    struct msg *msg_last;       /* last message in queue,unused */
    __kernel_time_t msg_stime;  /* last msgsnd time */
    __kernel_time_t msg_rtime;  /* last msgrcv time */
    __kernel_time_t msg_ctime;  /* last change time */
    unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes; /* ditto */
    unsigned short msg_cbytes;  /* current number of bytes on queue */
    unsigned short msg_qnum;    /* number of messages in queue */
    unsigned short msg_qbytes;  /* max number of bytes on queue */
    __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
};

消息队列的本质是一个结构体链表 发消息时往链表中放一个结构体 收消息时从里面依据msgtyp拿一个结构体。

这里写图片描述

消息队列的特性

1、生命周期随内核
2、双向通信
3、按块大小读写//不像管道是面向字节流的
4、内核保证同步与互斥。

代码:

server.c
#include"comm.h"
int main()
{
     int msgid=CreateMsgQueue();
     char buf[1024]={0};
    while(1)
     {
          RecvMsg(msgid,CLIENT_TYPE,buf);
          printf("client say :%s\n",buf);
          ssize_t s=read(0,buf,sizeof(buf)-1);
          if(s>0)
          {
               buf[s-1]=0;
               SendMsg(msgid,SERVER_TYPE,buf);
          }
     }
     Destroy(msgid);
     return 0;
}

client.c
#include"comm.h"
int main()
{
     int msgid=GetMsgQueue();
     char buf[1024]={0};
    while(1)
     {   
           ssize_t w = read(0,buf,sizeof(buf)-1);
          if(w>0)
          {
            buf[w-1]=0;
           SendMsg(msgid,CLIENT_TYPE,buf);
     }

          RecvMsg(msgid ,SERVER_TYPE,buf);
          printf("Server say:%s\n",buf);
     }
     return 0;
}

comm.c
#include"comm.h"
int ComMsgQueue(int flag)
{
     key_t key=ftok(PATHNAME,PROJ_ID);
     if(key<0)
     {
          perror("ftok");
          return -1;
     }
     int msgid=msgget(key,0666|flag);
     if(msgid<0)
     {
          perror("msgget");
          return -2;
     }
     return msgid;
}
int CreateMsgQueue()
{
   return ComMsgQueue(IPC_CREAT|IPC_EXCL);
}
int GetMsgQueue()
{
     return ComMsgQueue(IPC_CREAT);
}
int DestroyMsgQueue(int msgid)
{

     if(msgctl(msgid,IPC_RMID,NULL)<0)
     {
          perror("msgctl");
          return -1;
     }
    return 0;
}
int SendMsg(int msgid,int type,const char*msg)
{
   struct msgbuf _mb;
   _mb.mytype=type;
   strcpy(_mb.mtext,msg);
   if(msgsnd(msgid,&_mb,sizeof(_mb.mtext),0)<0)
   {
     perror("msgsnd");
     return -1;
   }
   return  0;
}
int RecvMsg(int msgid,int type,char*out)
{
   struct msgbuf _mb;
   if(msgrcv(msgid,&_mb,sizeof(_mb.mtext),type,0)<0)
   {
        perror("msgrcv");
        return -1;
   }
   strcpy(out,_mb.mtext);
   return 0;
}

comm.h
#ifndef _COMM_H_
#define _COMM_H_
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define PATHNAME "."
#define PROJ_ID 0x6666
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdio.h>
struct msgbuf
{
     long mytype;
     char mtext[1024];
};
int CreatMsgQueue();
int DestroyMsgQueue();
int SendMsg();
int RecvMsg();
int GetMsgQueue();
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值