2

有很多 fd 无法读取(例如,侦听套接字)。如何测试readfd 上的 (2) 是否会返回EINVAL,而不会冒险取出数据?

不起作用的事情:

  1. 我们可以read()使用传递的零字节缓冲区来做 a。但这被排除在外:

    read()允许但不要求实现对零字节请求执行错误检查。[从 POSIX 1003.1-2008]

  2. 我们可能很想调用select()描述符。不幸的是,select()可读集的语义非常重载,因此会告诉 fd 是“可读的”,而实际上调用它是错误的read()(例如,侦听套接字将被标记为“可读”但需要accept(),不read(),和还有其他不可移植的示例,例如 event 或 kqueue fds)。

  3. (某种工作)阅读您编译的每个平台的手册页,使用特定的系统调用测试 fd,以生成大致如下所示的函数:

    int isReadable(int fd)
    { return isActiveSocket(fd) || isFifo(fd) || isRegFile(fd) ||
               isEventFd(fd) || ... /* more pain */ }
    
  4. (某种作品)请注意,read()对于 fd 是否是系统调用的正确类型,它本身并不一定会给您一个很好的答案!令人惊讶的是,EINVAL在 POSIX 中未指定read()(STREAMS 除外),但在 linux 上提供给您(“EINVAL:fd 附加到一个不适合读取的对象”)并且在 BSD 上相当神秘(“EINVAL:与[fd] 是否定的。”)。

场景

有人启动了您的应用程序,您想知道值为 0 的 fd 是否是伪造的(例如侦听套接字),或者是否有可能从中读取。你不想尝试实际read()的 a) 因为那会阻塞,b) 因为在取出数据后你不能把它塞回去。

4

4 回答 4

2

不,您自己引用了规范的相关部分。

实际上,由于多种原因,读取可能随时失败。测试“将读取成功”,然后是read,仅引入了竞争条件 - 两次调用之间的情况可能会发生变化。

您需要以适当处理失败读取的方式编写应用程序。如果这样做,您通常不需要事先关心测试,并且可以简单地用于select确定数据何时(可能)可用。

于 2013-05-02T13:40:10.427 回答
2

您是否有理由不能将 fcntl 与 F_GETXFL 选项一起使用?

int isread(int fd)
{
   int o_accmode=0;
   int rc=fcntl(fd, F_GETXFL, &o_accmode);
   if(rc == -1 )
       return rc;
   rc=(o_accmode & O_ACCMODE);
   return (rc==O_RDONLY || rc==RDWR);
}
于 2013-05-02T16:10:21.360 回答
1

假设您可以调用 recv() 而不是 read() (它们通常可用于套接字),您可以在 flags 参数中提供 MSG_PEEK 并且 recv() 调用将照常运行,只是它实际上不会删除来自套接字内部缓冲区的任何字节。您可以使用它来捕获错误,而无需更改套接字的内部状态。

于 2013-03-09T02:16:56.797 回答
1

可能最简单的是使用select

int
is_valid_descriptor( int fd )
{
  fd_set rd;
  struct timeval t = { 0, 0 };
  FD_ZERO( &rd );
  FD_SET( fd, &rd );
  return -1 != select( fd + 1, &rd, NULL, NULL, &t );
}

请注意,这并不表示读取是否会阻塞(不清楚您所说的“成功”是什么意思)。

于 2013-03-08T21:09:40.267 回答