os.close(0)&的区别sys.stdin.close()

时间:2014-06-11 23:03:15

标签: python cgi stdin file-descriptor sys

我正在研究一些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崩溃)

1 个答案:

答案 0 :(得分:4)

我不确定,但我敢打赌,os.close()只是操作系统close()系统调用的python包装器。 Python文件对象为用户处理一些很酷的东西,比如内部缓冲数据和诸如此类的东西,这在调用它的close()方法时会得到解决。

最终,将在File对象的文件描述符上调用OS的close()系统调用。因此,在某个时候调用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()方法,因为它更干净,更蟒蛇。

更新

在查看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),并会多次调用它。