6

我阅读了这些man页面,我的理解是,如果write()失败并将 to 设置errnoEAGAINor EINTR,我可能会write()再次执行,所以我想出了以下代码:

  ret = 0;
  while(ret != count) {
    write_count = write(connFD, (char *)buf + ret,  count);
    while (write_count < 0) {
      switch(errno) {
        case EINTR:
        case EAGAIN:
          write_count = write(connFD, (char *)buf + ret,  count -ret);
          break;
        default:
          printf("\n The value of ret is : %d\n", ret);
          printf("\n The error number is : %d\n", errno);      
          ASSERT(0);
      }
    }
    ret += write_count;
  }

我正在执行read()write()处理套接字并处理与read()上述类似的事情。我正在使用带有gcc编译器的 Linux。

4

4 回答 4

6

你有一点“不要重复自己”的问题——不需要两个单独的调用write,也不需要两个嵌套循环。

我的正常循环看起来像这样:

for (int n = 0; n < count; ) {
    int ret = write(fd, (char *)buf + n, count - n);
    if (ret < 0) {
         if (errno == EINTR || errno == EAGAIN) continue; // try again
         perror("write");
         break;
    } else {
        n += ret;
    }
}

// if (n < count) here some error occurred
于 2012-04-26T19:45:05.567 回答
3

EINTREAGAIN处理通常应该略有不同。EAGAIN总是某种表示套接字缓冲区状态的瞬时错误(或者,更准确地说,您的操作可能会阻塞)。

一旦你击中了一个,EAGAIN你可能想睡一会儿或将控制权返回给一个事件循环(假设你正在使用一个)。

EINTR情况有点不同。如果您的应用程序不停地接收信号,那么这可能是您的应用程序或环境中的问题,因此我倾向于使用某种内部eintr_max计数器,因此我不会陷入我只是继续无限循环的理论情况上EINTR

Alnitak 的答案(对于大多数情况来说足够了)也应该保存在errno某个地方,因为它可能会被破坏perror()(尽管为简洁起见,它可能被省略了)。

于 2012-04-27T20:59:07.787 回答
2

我宁愿poll使用描述符,EAGAIN而不是无缘无故地忙于循环和烧毁 CPU。write对于我使用的非阻塞,这是一种“阻塞包装器” :

ssize_t written = 0;

while (written < to_write) {
    ssize_t result;
    if ((result = write(fd, buffer, to_write - written)) < 0) {
        if (errno == EAGAIN) {
            struct pollfd pfd = { .fd = fd, .events = POLLOUT };
            if (poll(&pfd, 1, -1) <= 0 && errno != EAGAIN) {
                break;
            }
            continue;
        }
        return written ? written : result;
    }
    written += result;
    buffer += result;
}
return written;

请注意,我实际上并没有检查poll返回值以外的结果;write如果描述符上存在永久性错误,我认为以下操作将失败。

您可能希望EINTR通过简单地将其添加到条件中来将其包含为可重试错误EAGAIN,但我更喜欢它来实际中断 I/O。

于 2015-08-03T04:05:15.607 回答
0

是的,有更简洁的使用方法:以 a作为参数write()的 write 函数类。FILE*也就是说,最重要的是,fprintf()fwrite()。在内部,这些库函数使用write()系统调用来完成它们的工作,并且它们处理类似EAGAINEINTR.

如果你只有一个文件描述符,你总是可以FILE*通过将它包装成 a fdopen(),这样你就可以将它与上面的函数一起使用。

但是,有一个陷阱:FILE*流通常是缓冲的。如果您正在与其他程序通信并等待它的响应,这可能是一个问题。即使没有逻辑错误,这也可能使两个程序都死锁,只是因为fprintf()决定推迟相应write()的位。fflush()您可以在实际需要执行write()调用时关闭缓冲或输出流。

于 2015-02-01T10:34:51.057 回答