【问题标题】:pselect gets interrupted occationallypselect 偶尔被打断
【发布时间】:2013-05-15 12:42:30
【问题描述】:

首先,我没有将其标记为关于“pselect”的问题的声誉,所以我选择了“select”。

我使用 pselect 来处理 UDP 套接字上的超时。代码如下:

UDP_STATUS udp_socket_recv(udp_socket_t* p_sock, int* p_bytes_rcvd)
{
    int res = 0;
    fd_set fds;
    struct timespec timeout;

    FD_ZERO(&fds);
    FD_SET(p_sock->m_socket, &fds);

    if (p_sock->m_timeout == NULL) {
        res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, NULL, NULL);
    } else {
        timeout.tv_sec = p_sock->m_timeout->tv_sec;
        timeout.tv_msec = p_sock->m_timeout->tv_usec * 1000;
        res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, &timeout, NULL);
    }

    if (res == 0)
        return UDP_TIMEOUT;
    else if (res == -1) {
        printf("pselect error: %s\n", strerror(errno)); /* Sometimes we end up here */
        return UDP_FAILURE;
    }

    res = recvfrom(p_sock->m_socket, ..); /* etc etc */
}

现在,上述方法在绝大多数 的情况下都可以正常工作(尽管我可能因为无法复制/粘贴而打错了某些内容)。但是 pselect 有时会返回 -1,而 strerror(errno) 调用会打印“Interrupted system call”。

我什至不确定这就是你想要的套接字超时方式,我什至不记得我是如何想出这个解决方案的......

非常感谢任何帮助。

【问题讨论】:

  • 嗯,这只是一个中断的系统调用。只需再次调用 pselect() ,这一次它会成功。 BTW if (p_sock->m_timeout == NULL) { 只有在 timeout 为 NULL 时才调用 pselect。在else { ... } 的情况下,您可能也想调用 pselect()。
  • 是的,很抱歉,我忘记了 else 子句中的 pselect。关于再次调用pselect,如果再次失败怎么办?

标签: c sockets select udp


【解决方案1】:

EINTR / 中断的系统调用不是错误情况,只是当您的程序在系统调用内部阻塞时发生的事情(信号已传递并可能已处理)。您可以忽略它并简单地循环,如下所示:(下面的程序不是最佳的,只是演示如何处理 EINTR)

UDP_STATUS udp_socket_recv(udp_socket_t* p_sock, int* p_bytes_rcvd)
{
int res = 0;
fd_set fds;
struct timespec timeout;

FD_ZERO(&fds);
FD_SET(p_sock->m_socket, &fds);

while(1) {
  if (p_sock->m_timeout == NULL) {
      res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, NULL, NULL);
  } else {
      timeout.tv_sec = p_sock->m_timeout->tv_sec;
      timeout.tv_msec = p_sock->m_timeout->tv_usec * 1000;
      res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, &timeout, NULL);
  }

  if (res > 0) break;
  if (res == 0)
    return UDP_TIMEOUT;

  switch(errno) {
  case EINTR: continue;
  default:
    printf("pselect error: %s\n", strerror(errno)); /* Sometimes we end up here */
    return UDP_FAILURE;
   }
 }

res = recvfrom(p_sock->m_socket, ..); /* etc etc */
}

【讨论】:

  • 这不处理EINTREAGAIN 是一个完全不同的野兽;这意味着没有数据可供读取。
  • 为您服务!谢谢。 (是的,我把它们弄混了!)
  • 这成功了。非常感谢你们所有人,这节省了我的一天(甚至可能是一周)。 :)
  • 我可以建议您阅读 WR Stevens。这确实是基本的东西(即使我一开始搞砸了)可能的错误返回也列在 select() 和 pselect() 的手册页中只需选择您的程序在特定情况下应该做什么,并可能添加另一种情况到开关。
【解决方案2】:

不,真的,只是重新启动系统调用。如果再次失败,则意味着您的进程收到了另一个信号。 EINTR,据我所知,很大程度上是早期 Unix 系统的实现产物,因此它们可以在阻塞系统调用的同时传递信号。关于这是一个聪明的还是愚蠢的设计决定,已经有大量的墨迹,但其结果是,当调用失败并显示 EINTR 时,代码生成阻塞系统调用需要重试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    相关资源
    最近更新 更多