如何在非套接字fd上进行非阻塞读取

时间:2018-12-07 16:54:27

标签: unix pipe nonblocking file-descriptor tty

有没有一种方法可以在管道/终端/等设备上以非阻塞模式进行 read(),也可以在{{1} }?

我需要这样做的原因是因为我无法找到保证recv(MSG_DONTWAIT)read()返回的文件描述符上的select() 不会< / em>块。

我知道可以使用poll()使文件描述符不阻塞,但这将全局更改该文件描述符的模式,而不仅仅是在调用线程/进程中。例如:

fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)

这也将使fd不受 的读写影响,这可能会使在同一fd上执行相反操作的另一个进程造成混乱,例如:

% 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

我想到的一种方法是在启动和non_blocking_read | filter | blocking_write 时保存文件状态标志,并在退出和SIGCONT时保存文件状态标志(这与termios设置一样),但是这是非常有限的,容易发生竞争,并且在程序异常退出的情况下会留下混乱。

在每个SIGTSTP之前/之后使用fcntl()进行保存/恢复也很丑陋,并且可能还会有其他问题。与read()之前的ioctl(FIONREAD)相同(我什至不确定它是否可以在任何fd上可靠地工作;不过,朝着这个方向的保证还是值得欢迎的。)

即使使用特定于系统的解决方案(例如linux或仅bsd),我也会感到满意。

供参考,here是关于在linux中修复它的讨论;但是,这个主意似乎并没有解决。

1 个答案:

答案 0 :(得分:0)

仅Linux的解决方案是通过以下方式重新打开文件描述符     “ / dev / stdin” |“ / dev / tty” |“ / dev / fd / $ fd”

C示例:

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

dup关联的文件描述符不同,重新打开的文件描述符将具有独立的文件状态标志。