进程间通信方式——消息队列

消息队列(System V)

作用

从一个进程向另外一个进程发送一个带有类型的数据块

本质

是存储在内核中的一个消息的队列(链表)

特点

  1. 每个数据块都被认为有一个类型,接受者进程接收的数据块可以有不同的类型值
  2. 和管道一样,每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总字节数也是有上限的(MSGMNB),系统上的消息队列总数也是有上限的(MSGMNI)
  3. 是一个全双工通信,可读可写。
  4. 生命周期随内核

查看消息队列:ipcs -q

IPC对象数据结构:/usr/include/linux/ipc.h

struct ipc_perm 
{ 
    __kernel_key_t  key; 
    __kernel_uid_t  uid; 
    __kernel_gid_t  gid; 
    __kernel_uid_t  cuid; 
    __kernel_gid_t  cgid; 
    __kernel_mode_t mode;  
    unsigned short  seq; 
};

消息队列结构:/usr/include/linux/msg.h  

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 */
	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 */
	__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 */
};

消息队列的系统限制:

进程间通信方式——消息队列_第1张图片

#define MSGMNI    16   /* <= IPCMNI */     /* max # of msg queue identifiers */
#define MSGMAX  8192   /* <= INT_MAX */   /* max size of message (bytes) */
#define MSGMNB 16384   /* <= INT_MAX */   /* default max size of a message queue */

消息队列结构:

进程间通信方式——消息队列_第2张图片

消息结构:

struct msgbuf {
    long mtype;         /* type of message */
    char mtext[1];      /* message text */
};     

使用

  1. 创建消息队列              msgget
  2. 发送数据/接收数据      msgsnd/msgrcv
  3. 释放消息队列              msgctl

msgget

创建或访问一个消息队列

key:内核中消息队列的标识

msgflg:

    IPC_CREAT    不存在则创建,存在则打开

    IPC_EXCL      与IPC_CREAT同时使用,存在则报错

    mode              权限     

 进程间通信方式——消息队列_第3张图片

msgrcv

从消息队列接收消息

成功后内核更新与该消息队列相关联的 msgid_ds 结构,刷新调用进程ID(msg_lrpid)、调用时间(msg_rtime)、并将消息数减1(msg_qnum)

msqid:由msgget返回的消息队列ID

msgp:指向准备接收的消息结构体msgbuf,需要自己定义

msgsz:msgp指向的要接收的数据长度,不包含msgtype

msgtype:指定接收的数据类型

  • type == 0 返回队列中第一个消息
  • type > 0 返回队列中消息类型为 type 的第一个消息
  • type < 0 返回队列中消息类型值 <= type绝对值的信息,优先取值最小的

msgflg:标志选项

  •   阻塞等待
  • IPC_NOWAIT   使操作不阻塞,没有消息返回-1,error设置为ENOMSG

msgsnd

msgctl

类似于 ioctl 垃圾桶函数

cmd

  • IPC_STAT    取队列的 msqid_ds 放入buf指向的结构体中
  • IPC_SET     将 msg_perm.uid、msg_perm.mode、msg_qbytes信息从buf结构体复制到这个队列的 msqid_ds 结构中
  • IPC_RMID   从系统中删除该消息队列的说有数据

ftok

通过文件的inode节点号和一个proj_id计算出一个key值

缺点:如果文件被删除或替换生,通过key值打开的不是同一个msgqueue

用消息队列实现一个简单的进程间 client server通信

client.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define IPC_KEY 0x12345678  //消息队列key
#define TYPE_SER 1  //数据块类型
#define TYPE_CLI 2  //数据块类型

struct msgbuf {
  long mtype;
  char mtext[1024];
};

int main()
{
  int msgid = -1;

  msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
  if (msgid < 0) {
    perror("msgget error");
    return -1;
  }
  while (1) {
    //接收数据
    //struct msgbuf 自己定义
    struct msgbuf buf;
    //msgid :操作句柄
    msgrcv(msgid, &buf, 1024, TYPE_CLI, 0);
    printf("client say:[%s]\n", buf.mtext);
    //发送数据
    memset(&buf, 0x00, sizeof(struct msgbuf));
    buf.mtype = TYPE_SER;
    scanf("%s", buf.mtext);
    msgsnd(msgid, &buf, 1024, 0);
  }
  //删除IPC
  msgctl(msgid, IPC_RMID, NULL);
  return 0;
}

server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define IPC_KEY 0x12345678  //消息队列key
#define TYPE_SER 1  //数据块类型
#define TYPE_CLI 2  //数据块类型

struct msgbuf {
  long mtype;
  char mtext[1024];
};

int main()
{
  int msgid = -1;

  msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
  if (msgid < 0) {
    perror("msgget error");
    return -1;
  }
  while (1) {
    struct msgbuf buf;
    //发送数据
    memset(&buf, 0x00, sizeof(struct msgbuf));
    buf.mtype = TYPE_CLI;
    scanf("%s", buf.mtext);
    msgsnd(msgid, &buf, 1024, 0);
    //接收数据
    msgrcv(msgid, &buf, 1024, TYPE_SER, 0);
    printf("server say:[%s]\n", buf.mtext);
  }
  msgctl(msgid, IPC_RMID, NULL);
  return 0;
}

进程间通信方式——消息队列_第4张图片

你可能感兴趣的:(Linux)