ROS源码学习分享_5_PollManager

        在上一章节中,我们了解了TopicManager节点启动背后的一些行为,本章我们将介绍同为控制节点的PollManager相关的一些源码行为。(本篇文章内容为作者观看源码后的主观理解,如若错误,敬请谅解)。

          PollManager的源代码如下:

void PollManager::start()
{
  shutting_down_ = false;
  thread_ = boost::thread(&PollManager::threadFunc, this);
}

    首先他将shutting_down_设置为false,这个变量设置为true意思为节点关闭。

 接下来他会启动一个线程来执行threadFunc。

        threadFunc函数的源代码如下:

                

void PollManager::threadFunc()
{
  disableAllSignalsInThisThread();

  while (!shutting_down_)
  {
    {
      boost::recursive_mutex::scoped_lock lock(signal_mutex_);
      poll_signal_();
    }

    if (shutting_down_)
    {
      return;
    }

    poll_set_.update(100);
  }
}

        首先他先屏蔽了所有信号,意味着这个线程不可中断。还记得我们在上一章讲的槽信号吗,在topic一端他将发布队列给到槽信号进行一个绑定,那么这里就是异步调用的另一端,他会调用槽信号函数,去处理发布队列。在调用一次之后他会判断节点是否关闭,没有关闭的话他会调用updata函数。我们再看一下PollSet::updata函数,它用于设置poll:

void PollSet::update(int poll_timeout)
{
  createNativePollset();

  // Poll across the sockets we're servicing
  boost::shared_ptr<std::vector<socket_pollfd> > ofds = poll_sockets(epfd_, &ufds_.front(), ufds_.size(), poll_timeout);
  if (!ofds)
  {
    if (last_socket_error() != EINTR)
    {
      ROS_ERROR_STREAM("poll failed with error " << last_socket_error_string());
    }
  }
  else
  {
    for (std::vector<socket_pollfd>::iterator it = ofds->begin() ; it != ofds->end(); ++it)
    {
      int fd = it->fd;
      int revents = it->revents;
      SocketUpdateFunc func;
      TransportPtr transport;
      int events = 0;

      if (revents == 0)
      {
        continue;
      }
      {
        boost::mutex::scoped_lock lock(socket_info_mutex_);
        M_SocketInfo::iterator it = socket_info_.find(fd);
        // the socket has been entirely deleted
        if (it == socket_info_.end())
        {
          continue;
        }

        const SocketInfo& info = it->second;

        // Store off the function and transport in case the socket is deleted from another thread
        func = info.func_;
        transport = info.transport_;
        events = info.events_;
      }

      // If these are registered events for this socket, OR the events are ERR/HUP/NVAL,
      // call through to the registered function
      if (func
          && ((events & revents)
              || (revents & POLLERR)
              || (revents & POLLHUP)
              || (revents & POLLNVAL)))
      {
        bool skip = false;
        if (revents & (POLLNVAL|POLLERR|POLLHUP))
        {
          // If a socket was just closed and then the file descriptor immediately reused, we can
          // get in here with what we think is a valid socket (since it was just re-added to our set)
          // but which is actually referring to the previous fd with the same #.  If this is the case,
          // we ignore the first instance of one of these errors.  If it's a real error we'll
          // hit it again next time through.
          boost::mutex::scoped_lock lock(just_deleted_mutex_);
          if (std::find(just_deleted_.begin(), just_deleted_.end(), fd) != just_deleted_.end())
          {
            skip = true;
          }
        }

        if (!skip)
        {
          func(revents & (events|POLLERR|POLLHUP|POLLNVAL));
        }
      }
    }
  }

        他先调用createNativePollset函数用于设置轮询集合。此函数构建一个用于轮询操作的套接字结构列表。这个列表将被传递给 POLL 函数,以监控不同的套接字事件。需要说明的是虽然他叫poll_sockets,但是他内部调用的是epoll.createNativePollset的源代码如下:

void PollSet::createNativePollset()
{
  boost::mutex::scoped_lock lock(socket_info_mutex_);

  if (!sockets_changed_)
  {
    return;
  }

  // Build the list of structures to pass to poll for the sockets we're servicing
  ufds_.resize(socket_info_.size());
  M_SocketInfo::iterator sock_it = socket_info_.begin();
  M_SocketInfo::iterator sock_end = socket_info_.end();
  for (int i = 0; sock_it != sock_end; ++sock_it, ++i)
  {
    const SocketInfo& info = sock_it->second;
    socket_pollfd& pfd = ufds_[i];
    pfd.fd = info.fd_;
    pfd.events = info.events_;
    pfd.revents = 0;
  }
  sockets_changed_ = false;
}

他会在上锁之后查询套接字有没有改变,没有改变则返回。如果改变了,就更新信息,并且将sockets_changed_信号设置为false;

  • info:包含套接字的详细信息(如文件描述符和事件类型)。
  • ufds_:存储 pollfd 结构,准备传递给 poll 系统调用。

ufds_ 中的 pollfd 结构是从 info 中提取和格式化的,用于轮询和检测事件。createNativePollset 函数的主要任务是将 info 中的套接字信息转换为 pollfd 结构,并填充到 ufds_ 中,以便在事件循环中进行处理。当套接字发生变化的时候,首先变化的是info,再由Info给实际事件轮询赋值

之后

boost::shared_ptr<std::vector<socket_pollfd> > ofds = poll_sockets(epfd_, &ufds_.front(), ufds_.size(), poll_timeout);

这句代码poll_sockets 函数执行实际的轮询操作。它会阻塞 poll_timeout 毫秒,也就是设置的100毫秒或直到事件发生。

        对于之后的updata.

  • 如果 poll_sockets 成功返回,它会返回一个包含发生事件的套接字列表的指针。通过遍历这些套接字,处理每个套接字的事件。
  • 对于每个发生事件的套接字,首先获取文件描述符 fd 和事件 revents
  • 如果 revents 为 0,跳过处理。
  • 使用互斥锁 socket_info_mutex_ 确保线程安全地访问 socket_info_ 映射。查找该文件描述符对应的 SocketInfo 对象。
  • 如果找到对应的 SocketInfo,提取其中的函数 func 和传输对象 transport,以及注册的事件 events
  • 检查是否有注册事件或者是 POLLERRPOLLHUPPOLLNVAL 事件,若是则调用注册的函数 func 处理事件。
  • 在处理错误事件时,如果套接字刚被删除,则跳过这些错误。

总结:在pollmanager中他启动poll_manager的回调函数,运行槽函数,并设置了一个100ms的时延,去轮询的执行套接字的回调函数,这个func是什么我们下章再讲。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值