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

edd*_*uld 2 python stdin cgi file-descriptor sys

我正在处理一些Python代码,这是从Apache调用的CGI脚本。

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

    for fd in [0, 1, 2]:
    try:
        os.close(fd)
    except Exception:
        pass
Run Code Online (Sandbox Code Playgroud)

通常,这可以正常工作,但是,如果它们没有打开,则会出现“ python.exe已停止工作”,“导致程序无法正常工作的问题”错误消息(Win32异常)。

几个问题:

  • 通过os.close(descriptor number)和sys.stdin.close()等关闭之间有什么区别?
  • 假设我应该通过两种机制关闭,如何检查描述符是否真正打开(即,调用os.close不会导致Python崩溃)

Ooz*_*ter 5

我不确定,但是我敢打赌,这os.close()只是操作系统close()系统调用的一个python包装器。Python文件对象为用户处理了一些很酷的事情,例如数据的内部缓冲等等,这在调用其close()方法时会得到照顾。

最终,close()将在File对象的文件描述符上调用OS的系统调用。因此,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
Run Code Online (Sandbox Code Playgroud)

如果可能的话,我建议调用sys.FD的close()方法,因为它更干净,更Python化。

更新资料

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

>>> import os

>>> f = open(os.devnull)
>>> f.close()
>>> f.close()
>>> f.close()
>>> f.close()
>>> f.close()
>>> print f.closed
True
Run Code Online (Sandbox Code Playgroud)

内部close_the_file(f);

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.");
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

/* 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;
}
Run Code Online (Sandbox Code Playgroud)

因此,os.close(fd)如果在同一文件描述符上调用两次,则调用应引发OSError。调用file.close()最终调用fclose(FILE *f)和处理它被称为多次。