Can I test if a file descriptor is of the right type for read()?

 ̄綄美尐妖づ 提交于 2019-12-11 02:29:56

问题


There are plenty of fds that can't be read from (for example, a listening socket). How do I test whether a read(2) on the fd will return EINVAL, without risking taking data out?

Things that don't work:

  1. We could do a read() with a zero-byte buffer passed. This is ruled out though:

    Implementations are allowed, but not required, to perform error checking for read() requests of zero bytes. [From POSIX 1003.1-2008]

  2. We might be tempted to call select() on the descriptor. Unfortunately, select() has very overloaded semantics for the readable set, so will tell that an fd is "readable" when in fact it's an error to call read() on it (for example, a listening socket will be marked "readable" but needs accept(), not read(), and there are other non-portable examples like event or kqueue fds).

  3. (Sort of works) Read manpages for every platform you compile on test the fd with specific system calls to produce a function that looks roughly like:

    int isReadable(int fd)
    { return isActiveSocket(fd) || isFifo(fd) || isRegFile(fd) ||
               isEventFd(fd) || ... /* more pain */ }
    
  4. (Sort of works) Note that read() itself doesn't necessarily give you a nice answer as to whether the fd was the right type for the system call! Surprisingly, EINVAL is unspecified in POSIX for read() (except on STREAMS), but is given to you on linux ("EINVAL: fd is attached to an object which is unsuitable for reading") and rather mysteriously on BSD ("EINVAL: The pointer associated with [the fd] was negative.").

Scenarios

Someone launches your application, and you want to know whether the fd with value 0 is bogus (eg a listening socket) or whether it's ever going to possible to read from it. You'd like to do without trying an actual read() a) because that blocks, b) because after taking the data out you can't stuff it back in.


回答1:


No. You quoted the relevant part of the specification yourself.

Indeed, a read may fail at any time for any number of reasons. Testing for "will read succeed", followed by a read, merely introduces a race condition - the situation may change in between the two calls.

You need to write your application in such a way that a failed read is handled appropriately. If you do that, you won't usually need to care about testing beforehand, and can simply use select to determine when data is (probably) available.




回答2:


Is there a reason you cannot use fcntl with the F_GETXFL option?

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);
}



回答3:


Probably the simplest is to use 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 );
}

Note that this does not indicate whether or not a read will block (it's not clear what you mean by "succeed").




回答4:


Assuming you're okay with calling recv() instead of read() (they are typically interchangeable for sockets), you can supply MSG_PEEK in the flags argument and the recv() call will act as usual except that it won't actually remove any bytes from the socket's internal buffer. You might be able to use that to catch errors without altering the socket's internal state.



来源:https://stackoverflow.com/questions/15294518/can-i-test-if-a-file-descriptor-is-of-the-right-type-for-read

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!