Python 读取umask(线程安全)

Python 读取umask(线程安全),python,thread-safety,umask,Python,Thread Safety,Umask,我知道用Python读取umask的模式: current_umask = os.umask(0) # line1 os.umask(current_umask) # line2 return current_umask # line3 但这不是线程安全的 在第1行和第2行之间执行的线程将具有不同的umask 有没有线程安全的方法来读取Python中的umask 相关:如果您的系统在/proc/[pid]/status中有Umask字段,您可以从中读取: impor

我知道用Python读取umask的模式:

current_umask = os.umask(0)  # line1
os.umask(current_umask)      # line2
return current_umask         # line3
但这不是线程安全的

在第1行和第2行之间执行的线程将具有不同的umask

有没有线程安全的方法来读取Python中的umask


相关:

如果您的系统在
/proc/[pid]/status
中有
Umask
字段,您可以从中读取:

import os

def getumask():
    pid = os.getpid()
    with open(f'/proc/{pid}/status') as f:
        for l in f:
            if l.startswith('Umask'):
                return int(l.split()[1], base=8)
        return None
根据CentOS 7.5和Debian 9.6进行测试


或者,您可以添加线程锁:)

子进程继承umask。您可以创建一个管道,派生一个子进程,在那里获取umask并将结果写入管道,以便父进程可以读取它

非常昂贵,但没有任何特殊要求,如
/proc
虚拟文件系统。下面是一个只有低级操作系统调用(所有异步安全)且没有错误检查的示例:

import os
import struct

def get_umask():
    pipe = os.pipe()
    pid = os.fork()
    if pid == 0:
        os.close(pipe[0])
        umask = os.umask(0)
        os.write(pipe[1], struct.pack('H', umask))
        os.close(pipe[1])
        os._exit(0)
    else:
        os.close(pipe[1])
        value = os.read(pipe[0], 2)
        os.close(pipe[0])
        os.waitpid(pid, 0)
        return struct.unpack('H', value)[0]

print("umask {:03o}".format(get_umask()))

可以通过创建临时文件并检查其权限来确定umask。这应适用于所有*nix系统:

def get_umask():
    import os, os.path, random, tempfile
    while True:
        # Generate a random name
        name = 'test'
        for _ in range(8):
            name += chr(random.randint(ord('a'), ord('z')))
        path = os.path.join(tempfile.gettempdir(), name)
        # Attempt to create a file with full permissions
        try:
            fd = os.open(path, os.O_RDONLY|os.O_CREAT|os.O_EXCL, 0o777)
        except FileExistsError:
            # File exists, try again
            continue
        try:
            # Deduce umask from the file's permission bits
            return 0o777 & ~os.stat(fd).st_mode
        finally:
            os.close(fd)
            os.unlink(path)

我所知道的唯一真正、明确的线程安全方式是调用一个新进程

import subprocess
umask_cmd = ('python', '-c', 'import os; print(os.umask(0777))')
umask = int(subprocess.check_output(umask_cmd))

注意,如果您有bash或其他shell,也可以调用它。由于它可能在一个奇怪的系统上,我选择在
umask_cmd
中使用python子进程,因为您必须使用python。如果您使用的是非怪异的*nix系统,那么您可以使用sh或bash。

首先为什么需要调用
os.umask()
?您通常不需要知道当前的umask。(另外请注意:在线程化环境中使用
os.umask(0)
不仅会带来竞争条件的风险,还会导致应用程序出现安全漏洞。您至少需要设置一个限制性掩码,如
os.umask(0o777)
@MartijnPieters为什么我想知道umask?我想比较两个环境,并检查两者之间的区别。我想改进我的工具dumpenv:这是一个比大多数人更好的理由来检查当前umask!而不是使用
os.getpid()
和插值,您只需从
/proc/self/status
中读取即可。如果有人想知道如何测试this@MartijnPietersredhat将此功能后端口到其rhel7/centos7 3.10内核。
/proc/self/status
中的umask是八进制的;此代码解析错误它是十进制的。使用
int(…,base=8)
。很好的解决方案!我喜欢这个。这段代码不安全。从
manfork(2)
:“在多线程程序中使用
fork()
后,在调用
execve(2)
之前,孩子只能安全地调用异步信号安全函数(请参见
信号安全(7)
)问题是Python解释器本身不是异步信号安全的。特别是,几乎任何Python代码都可能导致解释器分配内存,而且大多数内存分配函数都不是异步信号安全的。因此,调用
os.fork()永远都不安全
来自多线程程序。@abacabadabacaba我不确定。解释器自己的内存操作应该受到GIL的保护。@GIL的VPfB实现也不是异步信号安全的。3.Python试图使os.fork安全,甚至重新初始化子进程内的各种重要锁。这实际上可以为某些应用程序提供安全性pthread实现,例如glibc在Linux上使用的实现。但是,并非所有实现都能保证这一点,因为没有使用异步信号安全的函数保证fork之后是安全的。@MartijnPieters
mkstemp
不允许您指定新文件的权限,因此它不起作用。请使用python库“tempfile”,而不是“TMPDIR=os.environ.get('TMPDIR','/tmp')”umask值通常由子级继承,除非由pythonrc设置。