我想写一个类,它可以作为一个真正的文件描述符。它的.fileno()方法应该返回一个文件描述符,提供POSIX系统期望的所有服务。
这是我第一次接触POSIX系统编程,所以我可能会误解很多东西。
潜在的动机是希望使用内存中的Python对象作为stdin
或stdout
到subprocess.Popen
构造函数的变量,而不必依赖于临时文件或内存映射文件。但是我对能够完成工作的一些聪明的技巧不感兴趣——我真的希望有一个能够回答所有相关系统调用的Python实现。
你不能。POSIX文件描述符在Python世界之外的操作系统内核中被跟踪;你不能在Python代码中模拟它们。
如果您想要一个类在传递给系统调用时可以用作文件,那么它需要有一个fileno(),它是一个真正的操作系统文件描述符。一种不涉及硬盘的方法是使用管道,因为管道有文件描述符,然后系统调用可以写入这些文件描述符。
我确实写了一个类,使用这种技术做了另一个答案。它并没有真正做你想做的事情,但是使用管道的技术应该是可行的:
import io
import logging
import os
import select
import subprocess
import time
import threading
LOG_FILENAME = 'output.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
class StreamLogger(io.IOBase):
def __init__(self, level):
self.level = level
self.pipe = os.pipe()
self.thread = threading.Thread(target=self._flusher)
self.thread.start()
def _flusher(self):
self._run = True
buf = b''
while self._run:
for fh in select.select([self.pipe[0]], [], [], 0)[0]:
buf += os.read(fh, 1024)
while b'n' in buf:
data, buf = buf.split(b'n', 1)
self.write(data.decode())
time.sleep(1)
self._run = None
def write(self, data):
return logging.log(self.level, data)
def fileno(self):
return self.pipe[1]
def close(self):
if self._run:
self._run = False
while self._run is not None:
time.sleep(1)
os.close(self.pipe[0])
os.close(self.pipe[1])
这是我第一次接触POSIX系统编程,所以我可能会误解很多东西。
是的。
POSIX文件描述符只是数字——它们不是对象,所以你不能重写它们的方法。例如,0、1和2[通常]都是有效的文件描述符。
"相关的系统调用"是内置于Linux内核中的。Linux内核本身维护一个列表,该列表将文件描述符映射到一些内部内核对象(它确实有方法!),但您不能从Python插入新的文件描述符。在内核空间中运行的代码与正常的("用户模式")代码非常不同。
我建议你看一下子进程。PIPE,以及subprocess上的stdout/stdin/stderr属性或communication()方法。Popen对象?这将允许您启动子流程,读取它输出的数据,并完全控制发送给它的数据。(我认为这是你真正想做的……)如果您很好奇,那么当您使用这个程序时,您可以查看subprocess.py源代码,看看它是如何工作的。
有一个子进程的例子。管这里。
或者,如果您真的想在Python中实现一个完整的文件系统,请查看FUSE,它是Python绑定。FUSE包含一个在内核中运行的C模块,并处理对某个目录的文件系统请求。它通过将它们传递给可以用Python编写的用户空间程序来处理它们。您可以从单独的 Python程序打开这些文件,以获得它们的文件描述符。这有点复杂,可能不是初学者开始的最佳地方。