os.close(0) 与 sys.stdin.close() 的区别
我正在写一些Python代码,这段代码是一个通过Apache调用的CGI脚本。
这段代码的第一步是(我认为)尝试关闭标准输入、标准输出和标准错误,具体代码如下:
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 个回答
8
我不太确定,但我猜 os.close()
只是 Python 对操作系统的 close()
系统调用的一个封装。Python 的文件对象为用户处理了一些很酷的事情,比如内部的数据缓冲等,而这些在调用它的 close()
方法时就会处理好。
最终,操作系统的 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 的风格。
更新
在查看了 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)
,并且会处理多次调用的情况。