Python os.get_inheritable 函数
上次修改时间:2025 年 4 月 11 日
这份全面的指南探索了 Python 的 os.get_inheritable 函数,该函数用于检查文件描述符是否可以被子进程继承。我们将涵盖文件描述符标志、继承行为和实际示例。
基本定义
os.get_inheritable 函数检查文件描述符是否被标记为可继承。可继承的描述符会被传递给子进程。
关键参数:fd(要检查的文件描述符)。如果描述符可继承,则返回 True,否则返回 False。在 Python 3.4+ 中可用。
检查标准流继承
标准流(stdin、stdout、stderr)通常是可继承的。此示例检查它们的继承状态。
import os
import sys
# Check standard streams
print(f"stdin inheritable: {os.get_inheritable(sys.stdin.fileno())}")
print(f"stdout inheritable: {os.get_inheritable(sys.stdout.fileno())}")
print(f"stderr inheritable: {os.get_inheritable(sys.stderr.fileno())}")
# Check a regular file
with open("test.txt", "w") as f:
print(f"Regular file inheritable: {os.get_inheritable(f.fileno())}")
此示例首先检查标准流,通常返回 True。然后它检查一个新打开的文件,该文件通常继承默认的继承设置。
结果可能因平台和 Python 进程的启动方式而异。
修改继承标志
我们可以使用 os.set_inheritable 更改继承状态,并使用 os.get_inheritable 验证更改。
import os
# Create a temporary file
with open("temp.txt", "w") as f:
fd = f.fileno()
print(f"Original inheritable: {os.get_inheritable(fd)}")
# Disable inheritance
os.set_inheritable(fd, False)
print(f"After disabling: {os.get_inheritable(fd)}")
# Re-enable inheritance
os.set_inheritable(fd, True)
print(f"After re-enabling: {os.get_inheritable(fd)}")
这演示了如何更改文件描述符的继承标志并验证更改。修改后文件描述符仍然有效。
请注意,更改继承只会影响新的子进程,不会影响现有的子进程。
检查管道继承
可以使用 os.pipe 创建的管道来检查继承。此示例显示了管道的读取端和写入端。
import os
# Create a pipe
r, w = os.pipe()
print(f"Read end inheritable: {os.get_inheritable(r)}")
print(f"Write end inheritable: {os.get_inheritable(w)}")
# Modify one end
os.set_inheritable(r, False)
print(f"Modified read end: {os.get_inheritable(r)}")
# Clean up
os.close(r)
os.close(w)
这会创建一个管道并检查两端的继承状态。默认情况下,两端通常都是可继承的。然后,我们修改一端的状态。
始终记住在不再需要文件描述符时将其关闭。
子进程中的继承
此示例演示了继承如何影响实际的子进程行为。我们将创建一个文件并检查其在子进程中的可见性。
import os
import subprocess
# Create a test file
with open("shared.txt", "w") as f:
fd = f.fileno()
print(f"Before change: {os.get_inheritable(fd)}")
# Test with inheritable descriptor
subprocess.run(["python", "-c", "import os; print(os.path.exists('shared.txt'))"])
# Make non-inheritable and test again
os.set_inheritable(fd, False)
print(f"After change: {os.get_inheritable(fd)}")
subprocess.run(["python", "-c", "import os; print(os.path.exists('shared.txt'))"])
第一个子进程可以访问该文件,因为描述符是可继承的。禁用继承后,仍然可以通过文件系统访问该文件。
这显示了描述符继承和文件路径访问之间的区别。
与 fcntl 模块的比较
我们可以将 os.get_inheritable 与 fcntl 模块检查描述符标志的方法进行比较。
import os
import fcntl
with open("compare.txt", "w") as f:
fd = f.fileno()
# Using os.get_inheritable
print(f"os.get_inheritable: {os.get_inheritable(fd)}")
# Using fcntl
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
print(f"fcntl FD_CLOEXEC: {not bool(flags & fcntl.FD_CLOEXEC)}")
# Make them differ
os.set_inheritable(fd, False)
print("\nAfter change:")
print(f"os.get_inheritable: {os.get_inheritable(fd)}")
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
print(f"fcntl FD_CLOEXEC: {not bool(flags & fcntl.FD_CLOEXEC)}")
这显示了两种检查继承状态的方法。os.get_inheritable 更简单,而 fcntl 提供了更多的控制。
请注意,FD_CLOEXEC 是可继承的逆属性 - 设置后,描述符不可继承。
平台差异
此示例演示了 Unix 和 Windows 系统之间 os.get_inheritable 的平台特定行为。
import os
import sys
def check_inheritance(fd):
try:
return os.get_inheritable(fd)
except (AttributeError, OSError) as e:
return f"Error: {e}"
# Check standard streams
print("Platform:", sys.platform)
print(f"stdin: {check_inheritance(sys.stdin.fileno())}")
print(f"stdout: {check_inheritance(sys.stdout.fileno())}")
print(f"stderr: {check_inheritance(sys.stderr.fileno())}")
# Check invalid descriptor
print(f"Invalid fd: {check_inheritance(9999)}")
此代码检查不同平台上的继承并处理潜在的错误。对于某些描述符,Windows 的行为可能与类 Unix 系统不同。
该示例还展示了无效文件描述符的错误处理。
安全注意事项
- 描述符泄露: 可继承的描述符可能会泄露到子进程
- 权限提升: 粗心的继承可能会造成安全漏洞
- 默认设置: 了解您的平台的默认继承
- 清理: 在创建进程之前关闭不必要的描述符
- 平台差异: 行为在操作系统之间有所不同
最佳实践
- 显式控制: 有意设置继承标志
- 最小化继承: 仅传递需要的描述符
- 使用上下文管理器: 确保正确清理资源
- 检查文档: 了解平台特定的行为
- 与 subprocess 结合使用: 适当时使用 pass_fds 参数
资料来源
作者
列出所有 Python 教程。