用 C++ 包装 POSIX 消息队列是否有 ABI 问题?

2016-10-09 21:33:58 +08:00
 jjtx

我用 C++ 写了一个 “消息队列”类 MsgQueue ,来包装 Linux 上的消息队列操作,成员方法主要是 push (对应 msgsnd )和 pop (对应 msgrcv ),创建消息队列时没错,但在 msgsnd 和 msgrcv 时总是返回 -1 。我猜可能是把 Msg 的地址传进去时有 ABI 的问题,请知道的朋友指点一下,非常感谢!

程序如下,分别是定义消息队列的头文件,发送者,接收者以及 Makefile.

// msgqueue.h

#ifndef MSGQUEUE_H
#define MSGQUEUE_H

#include <stdexcept>
#include <string>
#include <cstring>
#include <cstdio>

#include <sys/msg.h>

struct Msg {
  long type;
  char text[BUFSIZ];

  Msg() {};

  Msg(long typ, const char *txt) : type(typ) {
    strncpy(text, txt, BUFSIZ - 1);
    text[BUFSIZ - 1] = '\0';
  }
};

class MsgQueue {
private:
  int id_;

public:
  MsgQueue(key_t key = 1235) {
    int id = msgget(key, IPC_CREAT);
    if (id == -1) {
      throw std::runtime_error("Create message queue failed.");
    }

    printf("Message queue's id is %d.\n", id_);
    id_ = id;
  }

  void push(const std::string &text, long type = 1) {
    push(Msg(type, text.c_str()));
  }

  void push(const char *text, long type = 1) {
    push(Msg(type, text));
  }

  void push(const Msg &msg) {
    if (-1 == msgsnd(id_, &msg, BUFSIZ, 0)) { // 总返回 -1
      throw std::runtime_error("Send message failed.");
    }
  }

  Msg pop(long type = 1) {
    Msg msg;
    if (-1 == msgrcv(id_, &msg, BUFSIZ, type, 0)) {
      throw std::runtime_error("Receive message failed.");
    }
    return msg;
  }
};

#endif
// send.cpp

#include <iostream>

#include "msgqueue.h"

int main() {
  MsgQueue Q;
  std::string txt = "hell";
  Q.push(txt.c_str());
  std::cout << "Send message: " << txt << std::endl;

  return 0;
}
// recv.cpp

#include <iostream>

#include "msgqueue.h"

int main() {
  MsgQueue Q;
  Msg msg = Q.pop();
  std::cout << "Receive message: " << msg.text << std::endl;

  return 0;
}
TARGET := $(patsubst %.cpp, %, $(wildcard *.cpp))

all: $(TARGET)

%: %.cpp $(wildcard *.h)
	g++ $< -std=c++14 -g -o $@

clean:
	rm -fr $(TARGET)
2156 次点击
所在节点    C
3 条回复
matthewgao
2016-10-09 23:31:50 +08:00
-1 的时候检查下 errno 看看返回什么结果,就一目了然了,会不会是读写权限的问题
matthewgao
2016-10-09 23:32:08 +08:00
The msgsnd() and msgrcv() system calls are used, respectively, to send messages to, and receive messages from,
a System V message queue. The calling process must have write permission on the message queue in order to
send a message, and read permission to receive a message.
Tony8Finet
2016-10-10 23:36:25 +08:00
System V IPC 是存在于作业系统中,因此其生命周期是和作业系统一样。一但建立了并未明确移除就一直存在,直到作业系统关机或重启。

你的程式有些问题:

* IPC\_CREATE 只能第一个人做 (注意存取权限),随后的人只能使用,不可再用 IPC\_CREATE:
<pre><code>MsgQueue(key_t key = 1235) {
int id = msgget(key, IPC_CREAT|0660);
created_ = (id == 0); // private: bool created_;
key_ = key; // private: key_t key_;
if (!created_) {
id = msgget(key, 0);
if (id == -1) {
throw std::runtime_error(std::string("msgget: ") +
strerror(errno));
}
}
id_ = id;
}
</code></pre>

* 建立者结束时要记得释放资源,若还有他人在使用中则系统会延迟到没人使用时才会释放:
<pre><code>~MsgQueue() {
if (created_) {
// 考虑释放资源,若不在此处释放则需另行手动处理 (ipcrm)
msgctl(key_, IPC_RMID, 0);
}
}
</code></pre>

有问题时程式应检查 errno 或用 strerror(errno)。

另外 IPC 的状态检视可用命令: `ipcs -a`

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/311486

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX