操作系统 使用消息队列的进程间通信

操作系统 使用消息队列的进程间通信

一个 消息队列 是一种进程间通信(IPC)机制,允许两个进程之间以消息的形式交换数据。它允许进程异步地通过向对方发送消息来进行通信,这些消息被存储在队列中,等待被处理,并在处理后被删除。

操作系统 使用消息队列的进程间通信

消息队列是在非共享内存环境中使用的缓冲区,任务通过相互传递消息而不是访问共享变量来进行通信。任务共享一个公共的缓冲池。消息队列是一个无界的FIFO队列,不同的线程之间通过并发访问进行保护。

事件是异步的。当一个类向另一个类发送事件时,它不会直接发送给目标反应类,而是将事件传递给操作系统消息队列。当目标类准备好处理时,它从消息队列的头部检索事件。同步事件可以使用触发操作来传递。

许多任务可以向队列中写入消息,但一次只能有一个任务从队列中读取消息。阅读器在消息队列上等待,直到有消息要处理。消息可以是任何大小。

消息队列的功能

我们将在程序中使用四个重要的函数来实现使用消息队列进行进程间通信。

1. int msgget (key_t key, int msgflg);

我们使用msgget函数来创建和访问消息队列。它接受两个参数。

  • 第一个参数是一个在系统中命名消息队列的键。
  • 第二个参数用于为消息队列分配权限,并将其与IPC_CREAT进行或运算以创建队列(如果该队列尚不存在)。如果队列已经存在,则IPC_CREAT将被忽略。成功时,msgget函数返回一个正数,即队列标识符;失败时,返回-1。

2. int msgsnd (int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

此函数允许我们将消息添加到消息队列中。

  • 第一个参数(msqid)是msgget函数返回的消息队列标识符。
  • 第二个参数是要发送的消息的指针,该指针必须以long int类型开头。
  • 第三个参数是消息的大小,它不包括long int消息类型。
  • 第四个和最后一个参数控制如果消息队列已满或系统对排队消息的限制已达到会发生什么。该函数在成功时返回0,并将消息数据的副本放入消息队列中。失败时,返回-1。

有两个与消息的结构相关的约束。首先,它必须小于系统限制,其次,它必须以long int开头。这个long int在接收函数中被用作消息类型。消息的最佳结构如下所示。

struct my_message 
{
long int message_type;
/* The data you wish to transfer */
}

由于message_type在消息接收中使用,您不能简单地忽略它。您必须声明您的数据结构以包含它,并且最好将其初始化为包含已知的值。

3. int msgrcv (int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

此函数从消息队列中检索消息。

  • 第一个参数(msgid)是由msgget函数返回的消息队列标识符。
  • 如上所述,第二个参数是指向要接收的消息的指针,该指针必须以long int类型开头。
  • 第三个参数是消息的大小。
  • 第四个参数允许实现优先级。如果值为0,则检索队列中的第一个可用消息。但是,如果值大于0,则检索具有相同消息类型的第一条消息。如果值小于0,则检索具有与msgtype的绝对值相同的类型值的第一条消息。简单来说,0值表示按照发送顺序接收消息,非零值表示接收具有特定消息类型的消息。
  • 最后一个参数控制当消息队列已满或达到了排队消息的系统限制时发生的情况。成功时,该函数返回0并将消息数据的副本放置在消息队列上。失败时,返回-1。

4. int msgctl (int msqid, int command, struct msqid_ds *buf);

最后一个函数是msgctl,它是控制函数。

  • 第一个参数是由msgget函数返回的标识符。
  • 第二个参数可以有以下三个值中的一个。
命令 描述
IPC_STAT 它将msqid_ds结构中的数据设置为反映与消息队列相关的值。
IPC_SET 如果进程有权限这样做,则将消息队列相关的值设置为msqid_ds数据结构中提供的值。
IPC_RMID 它删除消息队列。

msgctl函数在成功时返回0,在错误时返回-1。如果在进程等待在msgsnd或msgrcv函数中时删除了一个消息队列,则发送或接收函数将失败。

使用消息队列执行IPC的步骤

消息队列是在内核中存储的消息的链表,由消息队列标识符来标识。以下是使用消息队列进行通信的步骤。

操作系统 使用消息队列的进程间通信

  1. 通过msgget()创建新队列或打开现有队列。
  2. 通过msgsnd()将新消息添加到队列的末尾。每条消息都有一个正的长整型类型字段、一个非负的长度和实际的数据字节(与长度相对应),在将消息添加到队列时由msgsnd()指定。
  3. 通过msgrcv()从队列获取消息。我们不必按先进先出的顺序获取消息。相反,我们可以根据消息的类型字段获取消息。所有进程可以通过访问共享的系统消息队列来交换信息。发送进程将消息放置到另一个进程可以读取的队列中。每个消息都被赋予一个标识或类型,以便进程可以选择适当的消息。进程必须共享一个常用的密钥,才能首先访问队列。
  4. 对消息队列执行控制操作msgctl()。

示例

程序1: 让我们编写一个使用消息队列进行IPC的程序,将数据发送到消息队列。

#include
 #include
 #include
 #include
 #include
 #include
 #include
 #define MAX_TEXT 512   //maximum length of the message that can be sent allowed
 struct my_msg{
         long int msg_type;
         char some_text[MAX_TEXT];
 };
 int main()
 {
         int running=1;
         int msgid;
         struct my_msg some_data;
         char buffer[50]; //array to store user input
         msgid=msgget((key_t)14534,0666|IPC_CREAT);
         if (msgid == -1) // -1 means the message queue is not created
         {
                 printf("Error in creating queue\n");
                 exit(0);
         }

         while(running)
         {
                 printf("Enter some text:\n");
                 fgets(buffer,50,stdin);
                 some_data.msg_type=1;
                 strcpy(some_data.some_text,buffer);
                 if(msgsnd(msgid,(void *)&some_data, MAX_TEXT,0)==-1) // msgsnd returns -1 if the message is not sent
                 {
                         printf("Msg not sent\n");
                 }
                 if(strncmp(buffer,"end",3)==0)
                 {
                         running=0;
                 }
         }
 }

输出

以上程序的输出结果如下。

操作系统 使用消息队列的进程间通信

它是如何工作的?

结构体my_msg声明了一个long int变量和一个字符数组来存储发送到消息队列的数据。然后使用msgget()函数创建消息队列。接下来,使用fgets()函数从用户读取数据到缓冲区,然后将其复制到结构体some_data的变量some_text中。最后,使用msgsnd()函数将数据发送到队列中。使用strcmp函数通过比较数据的前三个字符来停止发送数据。如果数据以”end”开始,表示不再发送更多数据。

程序2: 现在,让我们编写一个使用消息队列进行IPC的程序,从上述创建的消息队列接收或读取消息。

#include
 #include
 #include
 #include
 #include
 #include
 #include
 struct my_msg{
         long int msg_type;
         char some_text[BUFSIZ];
 };
 int main()
 {
         int running=1;
         int msgid;
         struct my_msg some_data;
         long int msg_to_rec=0;
         msgid=msgget((key_t)12345,0666|IPC_CREAT);
         while(running)
         {
                 msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_rec,0);                 
                 printf("Data received: %s\n",some_data.some_text);
                 if(strncmp(some_data.some_text,"end",3)==0)
                 {
                         running=0;
                 }
         }
          msgctl(msgid,IPC_RMID,0);
 }

输出

上述代码给出以下输出。

操作系统 使用消息队列的进程间通信

它是如何工作的?

msg_to_rec变量被设置为0,以便接收的数据与发送的数据以相同的顺序接收。 while循环用于持续使用mgrcv()函数接收数据,直到接收到的文本是”end”为止,我们使用strcmp函数进行检查。数据是使用my_msg结构进行读取。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程