os.close(0) 和 sys.stdin.close() 之间的区别

问题描述 投票:0回答:2

我正在编写一些Python代码,它是从Apache调用的CGI脚本。

代码所做的第一件事是(我相信)尝试使用以下命令关闭 stdin/stdout/stderr:

    for fd in [0, 1, 2]:
    try:
        os.close(fd)
    except Exception:
        pass

通常这是有效的,但是如果它们没有打开,我会收到“python.exe 已停止工作”、“问题导致程序停止正常工作”错误消息(Win32 异常)。

几个问题:

  • 通过 os.close(描述符编号) 和 sys.stdin.close() 等关闭有什么区别
  • 假设我应该通过两种机制关闭,我如何检查描述符是否实际上打开(即调用 os.close 不会导致 Python 崩溃)
python cgi stdin file-descriptor sys
2个回答
8
投票

我不确定,但我敢打赌

os.close()
只是操作系统
close()
系统调用的 python 包装器。 Python 文件对象为用户处理一些很酷的东西,比如数据的内部缓冲等等,这在调用其
close()
方法时会得到处理。

最终,操作系统的

close()
系统调用将在 File 对象的文件描述符上调用。因此,在某个时刻调用
sys.stdin.close()
相当于调用
os.close(sys.stdin.fileno())

根据文档,您可以多次调用文件上的

close()
方法,Python 不会关心。文件对象甚至提供一个属性
closed
来检查文件是否打开:

>>> import os

>>> f = open(os.devnull)
>>> f.close()
>>> f.close()
>>> f.close()
>>> f.close()
>>> f.close()
>>> print f.closed
True

如果可能的话,我建议调用 sys.FD 的

close()
方法,因为它更干净、更Pythonic。

更新

查看 python 的源代码后,我发现了文件对象的这个(
fileobjects.c
):

static PyObject *
file_close(PyFileObject *f)
{
    PyObject *sts = close_the_file(f);
    if (sts) {
        PyMem_Free(f->f_setbuf);
        f->f_setbuf = NULL;
    }
    return sts;
}

PyDoc_STRVAR(close_doc,
"close() -> None or (perhaps) an integer.  Close the file.\n"
"\n"
"Sets data attribute .closed to True.  A closed file cannot be used for\n"
"further I/O operations.  close() may be called more than once without\n"
"error.  Some kinds of file objects (for example, opened by popen())\n"
"may return an exit status upon closing.");

内部 close_the_file(f);

static PyObject *
close_the_file(PyFileObject *f)
{
    int sts = 0;
    int (*local_close)(FILE *);
    FILE *local_fp = f->f_fp;
    char *local_setbuf = f->f_setbuf;
    if (local_fp != NULL) {
        local_close = f->f_close; // get fs close() method

        /* SOME CONCURRENCY STUFF HERE... */

        f->f_fp = NULL;
        if (local_close != NULL) {
            f->f_setbuf = NULL;
            Py_BEGIN_ALLOW_THREADS
            errno = 0;
            sts = (*local_close)(local_fp); // Call the close()
            Py_END_ALLOW_THREADS
            f->f_setbuf = local_setbuf;
            if (sts == EOF)
                return PyErr_SetFromErrno(PyExc_IOError);
            if (sts != 0)
                return PyInt_FromLong((long)sts);
        }
    }
    Py_RETURN_NONE;
}

文件的 close() 方法是什么?

static PyObject *
fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, char *mode,
                 int (*close)(FILE *))
{
    ...
    f->f_close = close;
    ...
}

对于 os 模块(使用
posixmodule.c
):

/* This file is also used for Windows NT/MS-Win and OS/2.  In that case the
   module actually calls itself 'nt' or 'os2', not 'posix', and a few
   functions are either unimplemented or implemented differently.  The source
   assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
   of the compiler used.  Different compilers define their own feature
   test macro, e.g. '__BORLANDC__' or '_MSC_VER'.  For OS/2, the compiler
   independent macro PYOS_OS2 should be defined.  On OS/2 the default
   compiler is assumed to be IBMs VisualAge C++ (VACPP).  PYCC_GCC is used
   as the compiler specific macro for the EMX port of gcc to OS/2. */

PyDoc_STRVAR(posix_close__doc__,
"close(fd)\n\n\
Close a file descriptor (for low level IO).");

/*
The underscore at end of function name avoids a name clash with the libc
function posix_close.
*/
static PyObject *
posix_close_(PyObject *self, PyObject *args)
{
    int fd, res;
    if (!PyArg_ParseTuple(args, "i:close", &fd))
        return NULL;
    if (!_PyVerify_fd(fd))
        return posix_error();
    Py_BEGIN_ALLOW_THREADS
    res = close(fd);  // close the file descriptor fd
    Py_END_ALLOW_THREADS
    if (res < 0)
        return posix_error(); // AKA raise OSError()
    Py_INCREF(Py_None);
    return Py_None;
}

如果文件描述符已关闭,则会引发
OSError

static PyObject *
posix_error(void)
{
    return PyErr_SetFromErrno(PyExc_OSError);
}

因此,如果在同一个文件描述符上调用两次,则调用

os.close(fd)
应该会引发 OSError 。调用
file.close()
最终会调用
fclose(FILE *f)
并将处理它被多次调用。


0
投票

sys.stdin.close() 不会关闭文件描述符

例如:

echo example | python3 -c 'import os, sys; sys.stdin.close(); print(os.read(0, 10))'

os.close(0)
则关闭底层文件描述符。

如果您不使用标准输入,最好关闭文件描述符,以便在其他程序写入时使错误显而易见。 (如果未使用 stdout 和 stderr,则类似。)

© www.soinside.com 2019 - 2024. All rights reserved.