【Linux】进程间通信------消息队列

我们知道进程是一个具有独立功能的程序关于某个数据结合的一次活动,是系统进行资源分配和调度运行的基本单位
每个进程是相互独立的,没有关联,不能在一个进程中直接去访问另一进程的资源。
那么问题来了,不同的进程之间肯定要进行信息交互和传递,所以我们就必须进程间通信(IPC),那么他们是怎么通信,方式有哪些?

进程间通信的目的:
  1. 数据传输:一个进程要将数据发给另一个进程。
  2. 资源共享:多进程之间共享相同资源。
  3. 通知事件:一个进程要想另一个进程或者进程组发送消息,通知发生了的事件(例如:僵尸进程的问题:子进程退出时要给父进程发送信息,但是没有发送,就造成这种情况)
  4. 进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并且及时知道他的状态改变。
进程间通信方式:
  1. 管道:匿名管道,命名管道
  2. 消息队列,共享内存,信号。

消息队列:

理解消息队列:消息队列提供了一个进程向另一个进程发送一块数据块的方法,每个数据块都被认为是有类型的,接收者进程接收的数据块可以有不同的类型值。(内核提供了一个链表,基于这个链表实现一个有类型的队列
这里写图片描述
这里写图片描述

消息队列的特点:
  1. 面向数据报
  2. 可以用于任意进程间通信
  3. 生命周期随内核
  4. 全双工

消息队列函数:

1.

//创建和访问一个消息队列
int msgget(key_t key,int msgflag);

参数:
key:是这个消息队列在内核中的唯一标识符,不同的进程就是根据key找到同一个消息队列,然后通信。它由 ftok()产生。
msgflag:由权限标志构成,通常为 IPC_CREAT, IPC_EXCL;
返回值:(句柄);成功返回一个非负整数,即该消息队列的标识符(可以理解为根文件描述符一样);失败返回-1;

介绍ftok()函数:

为什么要用它,共享内存,消息队列,信号量它们三个都是找一个中间介质,来进行通信的,这种介质多的是。就是怎么区分出来,就像唯一一个身份证来区分人一样。你随便来一个就行,就是因为这。只要唯一就行,就想起来了文件的设备编号和节点,它是唯一的,但是直接用它来作识别好像不太好,不过可以用它来产生一个号。ftok()就出场了。ftok函数具体形式如下:

 key_t ftok(const char *pathname, int proj_id);

其中参数fathname是指定的文件名,这个文件必须是存在的而且可以访问的。id是子序号,它是一个8bit的整数。即范围是0~255。当函数执行成功,则会返回key_t键值,否则返回-1。

1、ftok根据路径名,提取文件信息,再根据这些文件信息及project ID合成key,该路径可以随便设置。
2、该路径是必须存在的,ftok只是根据文件inode在系统内的唯一性来取一个数值,和文件的权限无关。
3、proj_id是可以根据自己的约定,随意设置。这个数字,有的称之为project ID; 在UNIX系统上,它的取值是1到255;
例如:

#define PATH_NAME "/tmp"
#define PATH_ID 0X6666
key_t key = ftok(PATH_NAME,PATH_ID);

2.

//消息队列的控制函数
int msgctl(int msgid,int cmd,struct msqid_ds *buf);

参数:
msgid:由msgget函数返回的消息队列标识符。
cmd:将要采取的动作。有三个值可取: IPC_STAT(读取消息队列的数据结构msqid_ds,并将其储存在buf指定的地址中); IPC_SET(设置消息队列的数据结构msqid_ds中ipc_perm元素的值); IPC_RMID(从内核中删除消息队列)。
返回值:成功返回0,失败返回1。
3.

//把消息发送到消息队列
int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg)

参数:
msqid:消息队列描述符;
msgp:一个指针,指向要发送的消息;
msgsz:msgp指向的消息长度;
msgflg:表示发送消息的按照阻塞还是非阻塞的方式;0表示阻塞;IPC_NOWAIT表示非阻塞。
返回值:成功返回0,失败返回-1;
消息结构的参考格式:

struct msgbuf
{
    long mytype;
    char mtext[1];
}

4.

//从消息队列接收消息
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg)

参数:
msqid:消息队列描述符;
msgp:一个指针,指向准备接收的消息;
msgsz:msgp指向的消息长度;
msgtyp:可以实现接收优先级的简单形式;
msgflg:控制着队列中没有想要类型的消息可供接收时候将要发生的事;一般可设为0;

重要指令:查看系统IPC资源命令:ipcs;删除IPC资源命令:ipcrm;
例如查看(删除)当前系统消息队列资源:
这里写图片描述

例子:利用消息队列实现client和sever两个进程的交互:
1,我们先看client和server的代码部分,

//server.c

#include "comm.h"
#include <string.h>

int main()
{
  int msgid = createMsgQueue(); //创建消息队列
  char buf[256]; //buf起到缓冲区的作用
  while(1)
  {
    //recv  从消息队列接收client的消息
    recvMsg(msgid,buf,CLIENT_TYPE);//接收消息,并把消息放到buf中 
    if(strcmp(buf,"quit") == 0)
    {
      printf("client is quit! me too!\n");
      break;
    }
    printf("client#%s\n",buf);
    //send  回应给client消息,并把消息发送到消息队列
    printf("Please Enter:");
    scanf("%s",buf); //把要回应给client的消息放到buf中
    sendMsg(msgid,buf,SERVRE_TYPE);//发送到消息队列

  }
  destroyMsgQueue(msgid);//销毁消息队列
  return 0;
}
//client.c

#include "comm.h"
#include <string.h>
int main()
{
  int msgid = getMsgQueue();//创建消息队列
  char buf[256];//buf起到缓冲区的作用
  while(1)
  {
    //send  client把消息发送到消息队列
    printf("Please Enter:");
    scanf("%s",buf); //把要发送的消息放到buf中
    sendMsg(msgid,buf,CLIENT_TYPE); //发送

    if(strcmp(buf,"quit") == 0)
    {
      printf("client is quit!\n");
      break;
    }
    //recv  从消息队列中接收server的回应
    recvMsg(msgid,buf,SERVRE_TYPE); //接收消息;并且把消息放到buf中
    printf("Server#%s\n",buf);
  }
  destroyMsgQueue(msgid); //销毁消息队列
  return 0;
}

2.函数的实现代码:

//comm.h
#ifndef _COMM_H_
#define _COMM_H_ 

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PATH_NAME "/tmp"  //使用ftok()创建消息队列描述符要使用的参数
#define PATH_ID 0x6666

#define SERVRE_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf{  //消息队列中的数据结构
  long mtype;
  char mtext[128];
};

int createMsgQueue(); //创建消息队列
int getMsgQueue();  //获取消息队列
int sendMsg(int msgid,char* msg,int type);  //发送消息到消息队列
int recvMsg(int msgid,char* msg,int type);  //从消息队列接收消息
void destroyMsgQueue(int msgid);  //销毁消息队列

#endif 
//comm.c

#include "comm.h"
#include <string.h>
//因为client和server都要创建相同的消息队列,所以他们的创建方式相同,所以这块代码可以写成一个函数实现代码复用
static int commMsgQueue(int flag)
{
  key_t k = ftok(PATH_NAME,PATH_ID);
  if(k < 0)
  {
    printf("ftok error!\n");
    return -1;
  }
  int msgid = msgget(k,flag);
  if(msgid < 0)
  {
    printf("msgget error!\n");
    return -2;
  }
  return msgid;
}

int createMsgQueue()
{
  return commMsgQueue(IPC_CREAT|IPC_EXCL|0644);
}

int getMsgQueue()
{
  return commMsgQueue(IPC_CREAT);
}

int sendMsg(int msgid,char* msg,int type)
{
  struct msgbuf buf;
  buf.mtype = type;
  strcpy(buf.mtext,msg);
  if(msgsnd(msgid,&buf,sizeof(buf.mtext),0) < 0)
  {
    printf("msgsnd error!\n");
    return -1;
  }
  return 0;
}

int recvMsg(int msgid,char* msg,int type)
{
  struct msgbuf buf;
  if(msgrcv(msgid,&buf,sizeof(buf.mtext),type,0) < 0)
  {
    printf("msgrcv error!\n");
    return -1;
  }
  strcpy(msg,buf.mtext);
  return 0;
}

void destroyMsgQueue(int msgid)
{
  if(msgctl(msgid,IPC_RMID,NULL) < 0)
  {
    printf("msgctl error!\n");
  }
}

运行结果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/prefect_boy/article/details/80387275