注册

Linux报 “no message queues available” 异常的原因以及解决办法

  1. 原因:

在 Linux 中,当一个进程第一次创建消息队列时(使用 msgget 函数),内核会给消息队列一个 System V IPC 标识符,该标识符会被用作以后消息队列的引用,例如使用 msgsnd 函数向消息队列发送消息或使用 msgrcv 函数从消息队列接收消息。如果系统中已经存在过多的消息队列(比如已经达到上限),就会出现报错信息 no message queues available

  1. 解决办法:

可以通过两种方式来解决此问题:

(1)增加系统消息队列的上限,可以通过修改 /proc/sys/kernel/msgmni/proc/sys/kernel/msgmax/proc/sys/kernel/msgmnb 等参数的值来实现,这些参数分别控制了消息队列的数量、消息队列中单个消息的最大长度和消息队列的总大小。例如,将 msgmni 的值增加到 8192,可以使用以下命令:

sysctl -w kernel.msgmni=8192

(2)合理使用消息队列。在使用过程中要注意消息队列的清理,避免出现大量废弃的消息队列。可以使用 ipcs 命令查看当前系统中存在的消息队列,并通过 ipcrm 命令来清理废弃的消息队列。例如,删除进程号为 12345 创建的消息队列,可以使用以下命令:

ipcrm -Q 12345

另外,还要注意程序中 msgget 函数的使用,在创建消息队列时要检查返回值,避免出现创建失败的情况。

参考示例:

可以使用以下程序来模拟创建多个消息队列的情况,运行时会出现 no message queues available 的错误:

#include 
#include 
#include 
#include 

#define MSGSIZE 64

int main()
{
  int i, ret, nqueues = 8192;
  int *keys = (int *)malloc(nqueues * sizeof(int));
  char *buf = (char *)malloc(MSGSIZE);
  struct msqid_ds info;
  for (i = 0; i < nqueues; i++)
  {
    /* 创建新的消息队列 */
    ret = msgget(IPC_PRIVATE, IPC_CREAT | 0600);
    if (ret == -1)
    {
      printf("msgget failed at %d!\n", i);
      break;
    }
    keys[i] = ret;
    printf("create queue: %d\n", ret);
  }

  /* 获取消息队列信息 */
  ret = msgctl(keys[0], IPC_STAT, &info);
  if (ret == -1)
  {
    printf("msgctl failed!\n");
  }
  else
  {
    printf("max msg size: %lu\n", info.msgmax);
    printf("current queue number: %lu\n", info.msg_qnum);
  }

  /* 发送消息 */
  ret = msgsnd(keys[0], buf, MSGSIZE, 0);
  if (ret == -1)
  {
    printf("msgsnd failed!\n");
  }

  /* 删除消息队列 */
  for (i = 0; i < nqueues; i++)
  {
    ret = msgctl(keys[i], IPC_RMID, NULL);
    if (ret == -1)
    {
      printf("msgctl failed at %d!\n", i);
    }
    else
    {
      printf("delete queue: %d\n", keys[i]);
    }
  }

  /* 释放资源 */
  free(keys);
  free(buf);

  return 0;
}

在运行该程序后,可以使用命令 ipcs -q 来查看当前系统中存在的消息队列数量,可以看到消息队列数量很快就到达了上限。通过增加 msgmni 的值或通过 ipcrm 来清理废弃的消息队列即可解决该问题。