如何在非套接字fd上进行非阻塞读取 [英] How to do a non-blocking read on a non-socket fd

查看:134
本文介绍了如何在非套接字fd上进行非阻塞读取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以在管道/终端/等设备上以非阻塞模式执行 read(),可以通过recv(MSG_DONTWAIT)在套接字上执行? >

我之所以需要这样做,是因为我无法找到保证select()poll()返回的文件描述符上的read()会被 not 阻止的任何保证. /p>

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

% 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

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

non_blocking_read | filter | blocking_write

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

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

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

作为参考,此处是有关在linux中修复它的讨论;但是,这个主意似乎并没有解决.

解决方案

仅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 liclic文件描述符不同,重新打开的filedescriptor将具有独立的文件状态标志.

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.

解决方案

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.

这篇关于如何在非套接字fd上进行非阻塞读取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆