问题
Is there a way to do a single read()
in non-blocking mode on a pipe/terminal/etc, the way I can do it on a socket with recv(MSG_DONTWAIT)
?
The reason I need that is because I cannot find any guarantee that a read()
on a file-descriptor returned as ready for reading by select()
or poll()
will not block.
I know can make the file descriptor non-blocking with fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)
but this will change the mode on that file descriptor globally, not just in the calling thread/process. For example:
% perl -MFcntl=F_SETFL,F_GETFL,O_NONBLOCK -e 'fcntl STDIN, F_SETFL, fcntl(STDIN, F_GETFL, 0) | O_NONBLOCK; select undef, undef, undef, undef'
^Z # put it in the background
% cat
cat: -: Resource temporarily unavailable
This will also make the fd non blocking for both reading and writing, which may confuse the hell out of another process doing the opposite on the same fd, as in:
non_blocking_read | filter | blocking_write
One way I think of is to save the file status flags on starting up and SIGCONT
, and restore them on exiting and on SIGTSTP
(just the way it's done with the termios settings), but this is very limited, race-prone, and will leave a mess behind in the case where the program exited abnormally.
Putting a save/restore with fcntl()
before/after each read()
also feels ugly and dumb, and may have other issues too. The same with an ioctl(FIONREAD)
just before the read
(which I'm not even sure it will work reliably with any fd; assurances in that direction will be welcome, though).
I would be happy even with system specific (eg. linux or bsd-only) solutions.
For reference, here is a discussion about fixing it in linux; the idea didn't seem to get anywhere, though.
回答1:
A Linux only solution would be to reopen the file descriptor via "/dev/stdin"|"/dev/tty"|"/dev/fd/$fd".
C example:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd;
char buf[8];
int flags;
if(0>(fd=open("/dev/stdin", O_RDONLY))) return 1;
if(0>(flags = fcntl(fd,F_GETFL))) return 1;
if(0>(flags = fcntl(fd,F_SETFL,flags|O_NONBLOCK))) return 1;
sleep(3);
puts("reading");
ssize_t nr = read(fd, buf, sizeof(buf));
printf("read=%zd\n", nr);
return 0;
}
Unlike a dup
licated file descriptor, a reopened filedescriptor will have independent file status flags.
来源:https://stackoverflow.com/questions/53673808/how-to-do-a-non-blocking-read-on-a-non-socket-fd