【问题标题】:mq_notify not notifying about an event (Linux programming)mq_notify 不通知事件(Linux 编程)
【发布时间】:2011-05-18 08:45:11
【问题描述】:

我正在使用mq_notify 来获得有关消息队列上事件的通知,但我注册的通知函数没有被调用。我错过了什么吗?

我在下面粘贴我的代码 sn-p:


static void sigNotifier(union sigval sv)
{
   printf ("I'm called.\n");
}    

int main()
{
   mqd_t queueID = 0;
   message_t msg;
   int retval;
   struct mq_attr attr;
   struct sigevent sev;

   attr.mq_msgsize = MSG_SIZE;
   attr.mq_maxmsg = 30;

   errno = 0;

   queueID = mq_open(MSG_QUEUE_NAME, O_RDONLY, 0666, &attr);

   if (queueID == -1) {
      printf ("Message queue open failed: %d\n", errno);
   }

   sev.sigev_notify = SIGEV_THREAD;
   sev.sigev_notify_function = sigNotifier;
   sev.sigev_notify_attributes = NULL;
   sev.sigev_value.sival_ptr = &queueID;

   retval = mq_notify(queueID, &sev);
   if (retval < 0) {
      printf ("Notification failed: %d\n", errno);
   }

   while (1);    
}

【问题讨论】:

  • struct mq_attr attr = {0}; and struct sigevent sev = {0}; 有什么不同吗?您没有初始化这些结构中的每个字段,您可能正在传递垃圾值。

标签: c linux posix ipc message-queue


【解决方案1】:

手册页mq_notify

1) 消息通知仅在新消息到达并且队列之前为空时发生。如果在调用 mq_notify() 时队列不为空,则只有在队列清空并且有新消息到达后才会发生通知。

2) 通知发生一次:通知发送后,取消通知注册,另一个进程可以注册消息通知。如果被通知的进程希望接收下一个通知,它可以使用 mq_notify() 请求进一步的通知。

3) 只能注册一个进程来接收来自消息队列的通知。

所以:

1) 使用 mq_notify() 注册后,立即清空 MsgQ 以在阅读器进程中接收新消息。

2) 在通知函数中重新注册以接收下一条消息。

3) 只注册1个进程来接收来自Q的消息。

这是一个简单的 C++ 消息队列阅读器代码:

#include <iostream>
#include <mqueue.h>
#include <string.h>
#include <sstream>
#include <unistd.h>
#include <errno.h>

using namespace std;

#define MSG_Q_NAME "/MY_MSGQ_3"

static void                     /* Thread start function */
tfunc(union sigval sv)
{
  mqd_t msq_id = *(static_cast<mqd_t*>(sv.sival_ptr));

  struct mq_attr attr;
  if(mq_getattr(msq_id, &attr) < 0)
  {
    cout << "Error in mq_getattr " << strerror(errno)  << endl;
    return;
  }

  // Reregister for new messages on Q
  struct sigevent sev;
  sev.sigev_notify = SIGEV_THREAD;
  sev.sigev_notify_function = tfunc;
  sev.sigev_notify_attributes = NULL;
  sev.sigev_value.sival_ptr = sv.sival_ptr;
  if (mq_notify(msq_id, &sev) < 0)
  {
    cout << "Error during Reregister in msq_notify : "
         << strerror(errno) << endl;
    exit(EXIT_FAILURE);
  }

  // Read new message on the Q
  char* arr = new char[attr.mq_msgsize];
  memset(arr, 0, attr.mq_msgsize);
  if(mq_receive(msq_id, arr, attr.mq_msgsize, 0) < 0)
  {
    if(errno != EAGAIN)
    {
      cout << "Error in mq_receive " << strerror(errno) << endl;
      exit(EXIT_FAILURE);
    }
  }
  else
  {
    cout << "Msg rcvd " << arr << endl;
  }
}

int main()
{
  mqd_t msq_id = mq_open(MSG_Q_NAME, O_RDONLY | O_NONBLOCK | O_CREAT, 0666, 0);
  if(msq_id == (mqd_t) -1)
  {
    cout << "Error on msg Q creation: " << strerror(errno) << endl;
    exit(EXIT_FAILURE);
  }

  // The process is registered for notification for new message on the Q
  struct sigevent sev;
  sev.sigev_notify = SIGEV_THREAD;
  sev.sigev_notify_function = tfunc;
  sev.sigev_notify_attributes = NULL;
  sev.sigev_value.sival_ptr = &msq_id;

  if (mq_notify(msq_id, &sev) < 0)
  {
    cout << "Error on msg Q notify : " << strerror(errno) << endl;
    exit(EXIT_FAILURE);
  }
  else
  {
    cout << "Notify for msg Q reception " << MSG_Q_NAME << endl;
  }

  // Man Page mq_notify: Message notification occurs only when a new
  // message arrives and the queue was previously empty. If the queue was
  // not empty at the time mq_notify() was called, then a notification will
  // occur only after the queue is emptied and a new message arrives.
  //
  // So emptying the Q to recv new messages
  ssize_t n = 0;
  struct mq_attr attr;
  if(mq_getattr(msq_id, &attr) < 0)
  {
    cout << "Error in mq_getattr " << strerror(errno)  << endl;
    exit(EXIT_FAILURE);
  }
  char* arr = new char[attr.mq_msgsize];
  memset(arr, 0, attr.mq_msgsize);
  while((n = mq_receive(msq_id, arr, attr.mq_msgsize, 0) >= 0))
  {
    cout << "Empty the Q. Msg rcvd " << arr << endl;
  }

  while(1)
    ;

  mq_close(msq_id);
}

这是一个简单的消息Q writer代码:

#include <iostream>
#include <mqueue.h>
#include <string.h>
#include <sstream>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h> 

using namespace std;

#define MSG_Q_NAME "/MY_MSGQ_3"

int main()
{
  struct mq_attr attr;
  memset(&attr, 0, sizeof attr);
  attr.mq_msgsize = 8192;
  attr.mq_flags = 0;
  attr.mq_maxmsg = 10;

  mqd_t msq_id = mq_open(MSG_Q_NAME, O_RDWR | O_CREAT | O_NONBLOCK,
                         0777, &attr);
  if(msq_id == (mqd_t) -1)
  {
    cout << "Error on msg Q creation: " << strerror(errno) << endl;
    exit(1);
  }

  // Write 5 msgs on message Q
  for(int i = 0; i < 5; ++i)
  {
    stringstream s;
    s << "My Msg " << i;

    if(mq_send(msq_id, s.str().c_str(), strlen(s.str().c_str()), 0) < 0)
    {
      if(errno != EAGAIN)
      {
        cout << "Error on sending msg on MsgQ " << strerror(errno);
        mq_close(msq_id);
        exit(1);
      }
    }
    else
    {
      cout << "Sent msg " << s.str() << endl;
    }

    sleep(1); // Easily see the received message in reader
  }

  mq_close(msq_id);
}

【讨论】:

    【解决方案2】:

    还要检查队列是否已经有消息。如果队列中存在消息,则 mq_notify() 将不会收到通知,直到队列为空然后有新消息进入。 由于队列是跨程序运行的持久性,因此您需要确保在打开之前已经在队列上调用了 mq_unlink()。

    【讨论】:

      【解决方案3】:

      因为代码是手册页的副本,所以我会问:

      1. 您确定将消息正确发送到队列吗?
      2. 首先尝试使用阻塞读取来读取,看看是否有什么收获。

      在 Linux 下,您可以使用 select/poll/epoll 来等待有关队列的通知以及 mqd_t 是普通的文件描述符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-20
        • 2021-10-29
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 2017-12-16
        • 1970-01-01
        • 2013-02-14
        相关资源
        最近更新 更多