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

你的名字 2022-05-22 01:24 410阅读 0赞

一、什么是消息

消息(message)是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列。

1、消息机制的数据结构

(1)消息首部

记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。

(2)消息队列头表

其每一项作为一个消息队列的消息头,记录了消息队列的有关信息,如指向消息队列中第一个消息和指向最后一个消息的指针、队列中消息的数目、队列中消息数据的总字节数、队列所允许消息数据的最大字节总数,还有最近一次执行发送操作的进程标识符和时间、最近一次执行接收操作的进程标识符和时间等。

2、消息队列的描述符

UNIX中,每一个消息队列都有一个称为关键字(key)的名字,是由用户指定的;消息队列有一消息队列描述符,其作用与用户文件描述符一样,也是为了方便用户和系统对消息队列的访问。

二、涉及的系统调用

下面的函数使用的头文件:

#include

#include

#include

1. msgget()

创建一个消息,获得一个消息的描述符。

系统调用格式:int msgqid=msgget(key,flag)

参数定义

key_t key;

int flag;

key是用户指定的消息队列的名字;flag是用户设置的标志和访问方式。如

IPC_CREAT |0400是否该队列已被创建。无则创建,是则打开;

IPC_EXCL |0400 是否该队列的创建应是互斥的。

msgqid 是该系统调用返回的描述符,失败则返回-1。

2. msgsnd()

发送一消息。向指定的消息队列发送一个消息,并将该消息链接到该消息队列的尾部。

系统调用格式:int msgsnd(msgqid,msgp,size,flag)

参数定义:

int msgqid,size,flag;

struct msgbuf * msgp;

其中msgqid是返回消息队列的描述符;msgp是指向用户消息缓冲区的一个结构体指针,包括消息类型和消息正文,即

{

long mtype; /*消息类型*/

char mtext[ ]; /*消息的文本*/

}

size指示由msgp指向的字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX()系统可调用参数来确定。

flag 规定当核心用尽内部缓冲空间时应执行的动作: 进程是等待,还是立即返回。若在标志flag中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd进程睡眠。若是设置IPC_NOWAIT,则在此情况下,msgsnd立即返回。

对于msgsnd(),核心须完成以下工作:

(1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;

(2)核心为消息分配消息数据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;

(3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向消息数据区的指针等数据;

(4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。

3. msgrcv()

接受一消息。从指定的消息队列中接收指定类型的消息。

系统调用格式:int msgrcv(msgqid,msgp,size,type,flag)

参数定义:

int msgqid,size,flag;

struct msgbuf *msgp;

long type;

其中,msgqid,msgp,size,flag与msgsnd中的对应参数相似,type是规定要读的消息类型,

flag规定倘若该队列无消息,核心应做的操作。如此时设置了IPC_NOWAIT标志,则立即返回,若在flag中设置了MS_NOERROR,且所接收的消息大于size,则核心截断所接收的消息。

对于msgrcv系统调用,核心须完成下述工作:

(1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回;

(2)根据type的不同分成三种情况处理:

type=0,接收该队列的第一个消息,并将它返回给调用者;

type为正整数,接收类型type的第一个消息;

type为负整数,接收小于等于type绝对值的最低类型的第一个消息。

(3)当所返回消息大小等于或小于用户的请求时,核心便将消息正文拷贝到用户区,并从消息队列中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。

4. msgctl( )

消息队列的操纵。读取消息队列的状态信息并进行修改,如查询消息队列描述符、修改它的许可权及删除该队列等。

系统调用格式:int msgctl(msgqid,cmd,buf);

参数定义:

int msgqid,cmd;

struct msgqid_ds *buf;

其中,函数调用成功时返回0,不成功则返回-1。buf是用户缓冲区地址,供用户存放控制参数和查询结果;cmd是规定的命令。命令可分三类:

(1)IPC_STAT。查询命令。如查询队列中的消息数目、队列中的最大字节数、最后一个发送消息的进程标识符、发送时间等;

(2)IPC_SET。按buf指向的结构中的值,设置和改变有关消息队列属性的命令。如改变消息队列的用户标识符、消息队列的许可权等;

(3)IPC_RMID。消除消息队列的标识符。

msgqid_ds 结构定义如下:

struct msgqid_ds

{

struct ipc_perm msg_perm; /*许可权结构*/

short pad1[7]; /*由系统使用*/

ushort msg_qnum;/*队列上消息数*/

ushort msg_qbytes; /*队列上最大字节数*/

ushort msg_lspid; /*最后发送消息的PID*/

ushort msg_lrpid; /*最后接收消息的PID*/

time_t msg_stime; /*最后发送消息的时间*/

time_t msg_rtime; /*最后接收消息的时间*/

time_t msg_ctime; /*最后更改时间*/

};

struct ipc_perm

{

ushort uid; /*当前用户*/

ushort gid; /*当前进程组*/

ushort cuid; /*创建用户*/

ushort cgid; /*创建进程组*/

ushort mode; /*存取许可权*/

{ short pid1;long pad2;} /*由系统使用*/

}

三、编写程序

创建两个子进程,子进程1创建一个消息队列,并使用消息机制向子进程2发送一条消息,发送结束后输出打印:Data sent! 子进程2接收到消息后,输出打印接收的消息字符,并输出打印:Data received!

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #include <sys/msg.h>
  8. #include <sys/ipc.h>
  9. #define MSGKEY 75
  10. struct msgform
  11. {
  12. long mtype;
  13. char mtext[1000];
  14. };
  15. void main( )
  16. {
  17. int p1,p2;
  18. while((p1=fork( ))==-1); //创建进程1
  19. if (p1>0) //父进程1
  20. {
  21. wait(NULL); //等待子进程1结束
  22. while((p2=fork( ))==-1); //创建进程2
  23. if(p2==0) //父进程2
  24. {
  25. wait(NULL); //等待子进程2结束
  26. exit(0);
  27. }
  28. else //子进程2
  29. {
  30. int msgqidRecs=msgget(MSGKEY,0777); //打开消息队列MSGKEY
  31. struct msgform msgRecs; //创建用于接收消息的缓冲区
  32. msgrcv(msgqidRecs,&msgRecs,1024,0,0);//接收消息
  33. printf("%s\n",msgRecs.mtext); //打印接收到的消息
  34. printf("Data received!\n");
  35. msgctl(msgqidRecs,IPC_RMID,0); //清楚消息队列,释放资源
  36. //printf("%d\n",msgqidRecs); //检查是否是目标消息队列
  37. }
  38. }
  39. else //子进程1
  40. {
  41. int msgqidSend=msgget(MSGKEY,IPC_CREAT|0777); //创建消息队列MSGKEY,返回给msgqidSend
  42. struct msgform msgSend; //创建用于发送消息的缓冲区
  43. strcpy(msgSend.mtext,"This is a message from child 2!"); //消设置息内容
  44. msgsnd(msgqidSend,&msgSend,1024,0); //发送消息
  45. printf("Data sent!\n");
  46. sleep(1);
  47. exit(1);
  48. //printf("%d\n",msgqidSend); //打印消息队列描述符
  49. }
  50. }

运行结果:

70

发表评论

表情:
评论列表 (有 0 条评论,410人围观)

还没有评论,来说两句吧...

相关阅读

    相关 进程通信消息队列

    对于进程我们知道一个进程拥有独立的用户地址空间,在一个进程中的全局变量在另一个进程中是看不到的,那仫进程间是如何通信的呢?在进程与进程间的通信必须要经过内核,在内核中开辟一块缓

    相关 Linux进程通信POSIX消息队列

    消息队列可认为是一个消息链表,它允许进程之间以消息的形式交换数据。有足够写权限的进程或线程可往队列中放置消息,有足够读权限的进程或线程可从队列中取走消息。每个消息都是一个记录,