How to do a non-blocking read on a non-socket fd

隐身守侯 提交于 2019-12-11 12:05:29

问题


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 duplicated 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

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