简体   繁体   English

在python中将stdout设置为非阻塞

[英]Setting stdout to non-blocking in python

Prior warning : I'm hacking around here out of curiosity. 事先警告 :出于好奇,我在这里乱砍。 I have no specific reason to do what I'm doing below! 我没有具体的理由去做我正在做的事情!

Below is done on Python 2.7.13 on MacOS 10.12.5 下面是在MacOS 10.12.5上的Python 2.7.13上完成的

I was hacking around with python and I thought it'd be interesting to see what happened if I made stdout nonblocking 我正在用python进行攻击,我觉得如果我做了stdout nonblocking会发生什么事情会很有趣

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

The call to fcntl is definitely successful. fcntl的调用肯定是成功的。 I then try to write a large amount of data (bigger than the max buffer size of a pipe on OSX - which is 65536 bytes). 然后我尝试写入大量数据(大于OSX上管道的最大缓冲区大小 - 这是65536字节)。 I do it in a variety of ways and get different outcomes, sometimes an exception, sometimes what seems to be a hard fail. 我以各种方式做到并获得不同的结果,有时是例外,有时似乎是一个很难的失败。

Case 1 情况1

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
try:
    sys.stdout.write("A" * 65537)
except Exception as e:
    time.sleep(1)
    print "Caught: {}".format(e)

# Safety sleep to prevent quick exit
time.sleep(1)

This always throws the exception Caught: [Errno 35] Resource temporarily unavailable . 这总是引发异常Caught: [Errno 35] Resource temporarily unavailable Makes sense I think. 我觉得这很有道理。 Higher level file object wrapper is telling me the write call failed. 更高级别的文件对象包装器告诉我写入调用失败。

Case 2 案例2

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
try:
    sys.stdout.write("A" * 65537)
except Exception as e:
    print "Caught: {}".format(e)

# Safety sleep to prevent quick exit
time.sleep(1)

This sometimes throws the exception Caught: [Errno 35] Resource temporarily unavailable or sometimes there is no exception caught and I see the following output: 这有时会引发异常Caught: [Errno 35] Resource temporarily unavailable或有时没有捕获异常,我看到以下输出:

close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

Case 3 案例3

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
try:
    sys.stdout.write("A" * 65537)
except Exception as e:
    print "Caught: {}".format(e)

# Safety sleep to prevent quick exit
time.sleep(1)

print "Slept"

This sometimes throws the exception Caught: [Errno 35] Resource temporarily unavailable or sometimes there is no exception caught and I just see "Slept". 这有时会引发异常Caught: [Errno 35] Resource temporarily unavailable或有时没有捕获异常,我只看到“睡眠”。 It seems that by print ing "Slept" I don't get the error message from Case 2. 似乎通过print “睡眠”我没有收到案例2中的错误消息。

Case 4 案例4

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
try:
    os.write(sys.stdout.fileno(), "A" * 65537)
except Exception as e:
    print "Caught: {}".format(e)

# Safety sleep to prevent quick exit
time.sleep(1)

Always okay! 总是好的!

Case 5 案例5

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
try:
    print os.write(sys.stdout.fileno(), "A" * 65537)
except Exception as e:
    print "Caught: {}".format(e)

# Safety sleep to prevent quick exit
time.sleep(1)

This is sometimes okay or sometimes prints the close failed in file object destructor error message. 这有时是好的,或者有时打印close failed in file object destructor错误消息中的close failed in file object destructor

My question is, why does this fail like this in python? 我的问题是,为什么在python中这样会失败? Am I doing something fundamentally bad here - either with python or at the system level? 我在这里做了一些根本不好的事情 - 使用python还是在系统级别?

It seems like somehow that writing too soon to stdout when the write already failed causes the error message. 当写入已经失败时,似乎以某种方式写入stdout太快会导致错误消息。 The error doesn't appear to be an exception. 该错误似乎不是一个例外。 No idea where it's coming from. 不知道它来自哪里。

NB I can write the equivalent program in C and it works okay: NB我可以在C中编写等效的程序,它可以正常工作:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/fcntl.h>
#include <unistd.h>

int main(int argc, const char * argv[])
{
    const size_t NUM_CHARS = 65537;
    char buf[NUM_CHARS];

    // Set stdout non-blocking
    fcntl(fileno(stdout), F_SETFL, O_NONBLOCK);

    // Try to write a large amount of data
    memset(buf, 65, NUM_CHARS);
    size_t written = fwrite(buf, 1, NUM_CHARS, stdout);

    // Wait briefly to give stdout a chance to be read from
    usleep(1000);

    // This will be written correctly
    sprintf(buf, "\nI wrote %zd bytes\n", written);
    fwrite(buf, 1, strlen(buf), stdout);
    return 0;
}

This is interesting. 这是有趣的。 There are a few things that I've found so far: 到目前为止,我发现了一些事情:

Case 1 情况1

This is because sys.stdout.write will either write all of the string or throw an exception, which isn't the desired behavior when using O_NONBLOCK . 这是因为sys.stdout.write将写入所有字符串或抛出异常,这在使用O_NONBLOCK时不是所需的行为。 When the underlying call to write returns EAGAIN (Errno 35 on OS X), it should be tried again with the data that is remaining. 当底层的write调用返回EAGAIN (OS X上的Errno 35)时,应该再次尝试使用剩余的数据。 os.write should be used instead, and the return value should be checked to make sure all the data is written. 应该使用os.write ,并检查返回值以确保写入所有数据。

This code works as expected: 此代码按预期工作:

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

def stdout_write(s):
    written = 0
    while written < len(s):
        try:
            written = written + os.write(sys.stdout.fileno(), s[written:])
        except OSError as e:
            pass

stdout_write("A" * 65537)

Case 2 案例2

I suspect that this error message is due to https://bugs.python.org/issue11380 : 我怀疑此错误消息是由https://bugs.python.org/issue11380引起的:

close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

I'm not sure why it's sometimes being called. 我不确定为什么它有时会被调用。 It may be because there is a print in the except statement which is trying to use the same stdout that a write just failed on. 这可能是因为在except语句中有一个print ,它试图使用相同的stdout来写入失败。

Case 3 案例3

This is similar to Case 1. This code always works for me: 这与案例1类似。此代码始终适用于我:

fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

def stdout_write(s):
    written = 0
    while written < len(s):
        try:
            written = written + os.write(sys.stdout.fileno(), s[written:])
        except OSError as e:
            pass

stdout_write("A" * 65537)

time.sleep(1)

print "Slept"

Case 4 案例4

Make sure you check the return value of os.write , I suspect that the full 65537 bytes are not being successfully written. 确保检查os.write的返回值,我怀疑没有成功写入完整的65537字节。

Case 5 案例5

This is similar to Case 2. 这与案例2类似。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM