1

我一直在搞乱一个使用 boost::asio 进行 UDP 和 SocketCAN 通信的应用程序。今天,我注意到一些奇怪的事情——它正在泄漏内存!

所以我抓住了我值得信赖的工具包,其中包括

echo 0 $(awk '/Private/ {print "+", $2}' /proc/`pidof main`/smaps) | bc

和 Allinea DDT 并开始诊断这个问题。

我最终得到的是以下片段,它利用 boost::asio::posix::basic_stream_descriptor 作为它的基础:

void Can::write(struct can_frame frame) {
  stream_.async_write_some(boost::asio::buffer(&frame, sizeof(frame)),
                           boost::bind(&Can::datSend, this)
  );
}

在这里,datSend 只是一个 ping 看门狗的空函数。我也试过

void Can::write(struct can_frame frame) {
  stream_.write_some(boost::asio::buffer(&frame, sizeof(frame)));
}

但这出于某种原因给出了异常(无效数据)。

这段代码的后端看起来像这样:

boost::asio::io_service ioService_;
boost::asio::posix::basic_stream_descriptor<> stream_;

Constructor() : stream_(ioService_) {
  socketDescriptor_ = socket(PF_CAN, SOCK_RAW, CAN_RAW);

  struct timeval timeout {
      .tv_sec = 5,
      .tv_usec = 0
  };

  if (setsockopt(socketDescriptor_, SOL_SOCKET, SO_RCVTIMEO,
                 reinterpret_cast<char *>(&timeout),
                 sizeof(timeout)) < 0) {
    throw std::string("Error setting CAN socket timeout");
  }

  strcpy(interfaceRequest_.ifr_name, interfaceName.c_str());
  ioctl(socketDescriptor_, SIOCGIFINDEX, &interfaceRequest_);
  socketAddress_.can_family = AF_CAN;
  socketAddress_.can_ifindex = interfaceRequest_.ifr_ifindex;
  stream_.assign(socketDescriptor_);

  if (bind(socketDescriptor_, (struct sockaddr *)&socketAddress_,
           sizeof(socketAddress_)) < 0) {
    throw std::string("Error in socket bind");
  }

}

之后我只运行 ioservice 就是这样:

void Can::iosThreadWorker() { ioService_.run(); }

我已经阅读了很多 stackoverflow 主题以及 boost 文档,但似乎无法找到为什么这个函数会泄漏内存。

Boost 版本 - 1.60 G++ - 6.30 操作系统:Ubuntu 17.04

4

1 回答 1

2

所以我挖得更深一点,发现了这个关于 boost.io_service 的花絮:

如果 io_service.run() 没有工作要做,则完成 - 因此通过运行它然后发送更多要完成的异步工作,工作没有完成。

这源于一个问题,有人翻转了 ioservice.run() 和异步读取回调分配以使代码看起来更好 - 现在在运行之前没有工作堆积,ioservice 可以完成它的工作并返回。

于 2017-09-26T13:11:21.497 回答