linux 进程间通信机制(IPC机制)一消息队列

时间:2021-01-22 16:15:45

  消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

一.头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

二.函数
1.ftok
(1)原型:

  key_t ftok( char * fname, int id )
(2)参数:
  fname:指定的文件名
id:子序号(虽然为int,但是只有8个比特被使用(0-255))
(3)返回值:

  成功 --- key_t值,失败 --- -1
(4)作用:

  系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

2.msgget
(1)原型:

  int msgget(key_t key, int msgflg)
(2)参数:
  key:来源于ftok返回的IPC键值(0(IPC_PRIVATE):会建立新的消息队列)
  msgflg:

      0666|IPC_CREAT --- 如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符
      0666|IPC_CREAT|IPC_EXCL --- 如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错
(3)返回值:

  成功 ---返回消息队列的标识符,失败 --- -1,错误原因存于error中
(4)作用:

  得到消息队列标识符或创建一个消息队列对象
(5)错误码:
  EACCES:指定的消息队列已存在,但调用进程没有权限访问它
  EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
  ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志
  ENOMEM:需要建立消息队列,但内存不足
  ENOSPC:需要建立消息队列,但已达到系统的限制

3.msgctl
(1)原型:

  int msgctl(int msqid, int cmd, struct msqid_ds *buf)
(2)参数:
  msqid:消息队列标识符
  cmd:

    IPC_STAT:获得msgid的消息队列头数据到buf中
    IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
    IPC_RMID:删除消息队列,将队列从系统内核中删除
    buf:消息队列管理结构体
(3)返回值:

  成功 --- 0,失败 --- -1,错误原因存于error中
(4)作用:

  获取和设置消息队列的属性
(5)错误码:
  EACCESS:参数cmd为IPC_STAT,确无权限读取该消息队列
  EFAULT:参数buf指向无效的内存地址
  EIDRM:标识符为msqid的消息队列已被删除
  EINVAL:无效的参数cmd或msqid
  EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行

3.msgsnd
(1)原型:

  int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
(2)参数:
  msqid:消息队列标识符
  msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型
    参照格式如下:
    struct s_msg{ /*msgp定义的参照格式*/
        long type; /* 必须大于0,消息类型 */
        char mtext[256]; /*消息正文,可以是其他任何类型*/
      } msgp;
    msgsz:要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
    msgflg:
      0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
      IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
      IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
(3)返回值:

  成功 --- 0,失败 --- -1,错误原因存于error中
(4)作用:

  将msgp消息写入到标识符为msqid的消息队列
(5)错误码:
  EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满
  EIDRM:标识符为msqid的消息队列已被删除
  EACCESS:无权限写入消息队列
  EFAULT:参数msgp指向无效的内存地址
  EINTR:队列已满而处于等待情况下被信号中断
  EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0

4.msgrcv
(1)原型:

  ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
(2)参数:
  msqid:消息队列标识符
  msgp:存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同
  msgsz:要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
  msgtyp:
    0:接收第一个消息
    >0:接收类型等于msgtyp的第一个消息
    <0:接收类型等于或者小于msgtyp绝对值的第一个消息
  msgflg:
    0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
    IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
    IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
    IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃
(3)返回值:

  成功 --- 0,失败 --- -1,错误原因存于error中
(4)作用:

  从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除
(5)错误码:
  E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR
  EIDRM:标识符为msqid的消息队列已被删除
  EACCESS:无权限读取该消息队列
  EFAULT:参数msgp指向无效的内存地址
  ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
  EINTR:等待读取队列内的消息情况下被信号中断

 

三.书上看到的例子:

1.msgrcv.c

#include <unistd.h>//ftok头函数  
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>//msg头函数

#define PATH "./msg"//指定文件路径
#define TEXT_SIZE 1024

struct msg_st
{
long int msg_type;
char text[TEXT_SIZE];
};

int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 2;//msgtype>0时,要和发送端的一致

key_t key = ftok(PATH,4);

msgid = msgget(key, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}

while(running)
{
if(msgrcv(msgid, (void*)&data, TEXT_SIZE+sizeof(long int), msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
exit(-1);
}
printf("You wrote: %s\n",data.text);

if(strncmp(data.text, "end", 3) == 0)
running = 0;
}

//删除消息队列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(-1);
}
exit(0);
}

2.msgsnd

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>

#define PATH "./msg"
#define TEXT_LEN 1024
struct msg_st
{
long int msg_type;
char text[TEXT_LEN];
};

int main()
{
int running = 1;
struct msg_st data;
char buffer[TEXT_LEN];
int msgid = -1;

key_t key = ftok(PATH,4);

msgid = msgget(key, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}

while(running)
{
printf("Enter some text: ");
fgets(buffer, TEXT_LEN, stdin);
data.msg_type = 2;//如果service端 msgtype>0, client端的msgtype要和service端一致
strcpy(data.text, buffer);

if(msgsnd(msgid, (void*)&data, TEXT_LEN+sizeof(long int), 0) == -1)
{
fprintf(stderr, "msgsnd failed\n");
exit(-1);
}

if(strncmp(buffer, "end", 3) == 0)
running = 0;
sleep(1);
}
exit(0);
}