使用open()打开文件时使用哪种共享模式?

7

我在open()函数的参数中没有看到任何允许指定文件共享方式的内容。因此,我怀疑文件将尽可能地被共享。具体来说:

  1. 当文件用于读取时,其共享模式将允许后续的打开操作以读取文件,但不允许写入。
  2. 当文件用于写入时,其共享模式将拒绝后续的打开操作以读取或写入文件。

这对我来说似乎是最合理的实现。我的假设正确吗?

更新:Martijn Pieters表示答案取决于操作系统。因此,就此问题而言,我的目标操作系统是Windows。


3
这完全取决于操作系统;你有什么想法吗? - Martijn Pieters
@MartijnPieters 我的目标操作系统是Windows。 - David Heffernan
在Linux上,不进行锁定,因此您可以随意打开多次以进行读写。但是,由于您说您正在使用Windows,我不会将其发布为答案。 - John Zwinck
2个回答

8
Python 在打开文件时内部使用 _wfopen() (Python 2 中使用 open() 函数) 或 _wopen() (Python 3 和 io.open()),两者都不允许指定任何共享标志。因此,共享被设置为默认值,而这个默认值似乎没有被记录在文档中。
如果您想自己设置共享模式,您需要使用 msvcrt.open_osfhandle() 来打开文件并指定共享模式。
Python 问题跟踪器中有一个 补丁 实现了一个 sharing 模块,演示了如何做到这一点。这个补丁中的 opener 稍微简化后是:
import os
import msvcrt
import _winapi

CREATE_NEW                  = 1
CREATE_ALWAYS               = 2
OPEN_EXISTING               = 3
OPEN_ALWAYS                 = 4
TRUNCATE_EXISTING           = 5
FILE_SHARE_READ             = 0x00000001
FILE_SHARE_WRITE            = 0x00000002
FILE_SHARE_DELETE           = 0x00000004
FILE_SHARE_VALID_FLAGS      = 0x00000007
FILE_ATTRIBUTE_READONLY     = 0x00000001
FILE_ATTRIBUTE_NORMAL       = 0x00000080
FILE_ATTRIBUTE_TEMPORARY    = 0x00000100
FILE_FLAG_DELETE_ON_CLOSE   = 0x04000000
FILE_FLAG_SEQUENTIAL_SCAN   = 0x08000000
FILE_FLAG_RANDOM_ACCESS     = 0x10000000
GENERIC_READ                = 0x80000000
GENERIC_WRITE               = 0x40000000
DELETE                      = 0x00010000
NULL                        = 0

_ACCESS_MASK = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
_ACCESS_MAP  = {os.O_RDONLY : GENERIC_READ,
                os.O_WRONLY : GENERIC_WRITE,
                os.O_RDWR   : GENERIC_READ | GENERIC_WRITE}

_CREATE_MASK = os.O_CREAT | os.O_EXCL | os.O_TRUNC
_CREATE_MAP  = {0                                   : OPEN_EXISTING,
                os.O_EXCL                           : OPEN_EXISTING,
                os.O_CREAT                          : OPEN_ALWAYS,
                os.O_CREAT | os.O_EXCL              : CREATE_NEW,
                os.O_CREAT | os.O_TRUNC | os.O_EXCL : CREATE_NEW,
                os.O_TRUNC                          : TRUNCATE_EXISTING,
                os.O_TRUNC | os.O_EXCL              : TRUNCATE_EXISTING,
                os.O_CREAT | os.O_TRUNC             : CREATE_ALWAYS}


def os_open(file, flags, mode=0o777,
            *, share_flags=FILE_SHARE_VALID_FLAGS):
    '''
    Replacement for os.open() allowing moving or unlinking before closing
    '''
    if not isinstance(flags, int) and mode >= 0:
        raise ValueError('bad flags: %r' % flags)

    if not isinstance(mode, int) and mode >= 0:
        raise ValueError('bad mode: %r' % mode)

    if share_flags & ~FILE_SHARE_VALID_FLAGS:
        raise ValueError('bad share_flags: %r' % share_flags)

    access_flags = _ACCESS_MAP[flags & _ACCESS_MASK]
    create_flags = _CREATE_MAP[flags & _CREATE_MASK]
    attrib_flags = FILE_ATTRIBUTE_NORMAL

    if flags & os.O_CREAT and mode & ~0o444 == 0:
        attrib_flags = FILE_ATTRIBUTE_READONLY

    if flags & os.O_TEMPORARY:
        share_flags |= FILE_SHARE_DELETE
        attrib_flags |= FILE_FLAG_DELETE_ON_CLOSE
        access_flags |= DELETE

    if flags & os.O_SHORT_LIVED:
        attrib_flags |= FILE_ATTRIBUTE_TEMPORARY

    if flags & os.O_SEQUENTIAL:
        attrib_flags |= FILE_FLAG_SEQUENTIAL_SCAN

    if flags & os.O_RANDOM:
        attrib_flags |= FILE_FLAG_RANDOM_ACCESS

    h = _winapi.CreateFile(file, access_flags, share_flags, NULL,
                           create_flags, attrib_flags, NULL)
    return msvcrt.open_osfhandle(h, flags | os.O_NOINHERIT)

1
好的,但是你知道最终传递给 CreateFile 的是什么吗?无论 Python 文件模式如何,它都是 FILE_SHARE_READ | FILE_SHARE_WRITE 吗? - David Heffernan
@DavidHeffernan:你是指Python 2中默认的open()命令吗?没有设置任何FILE_SHARE标志。实际上,open()命令使用了_wfopen() C API调用 - Martijn Pieters
1
这似乎不太对。如果没有设置FILE_SHARE标志,则文件会被独占打开。这是否归结于底层CRT不知道共享的事实?因此,我的问题实际上是关于_wfopen()的问题吗? - David Heffernan
我怀疑是这样的,是的。顺便提一下,io.open() 使用 _wopen,而且两个文档都没有说明设置了什么共享模式。 - Martijn Pieters
请注意,_wfopen()_wopen() 方法可能根据所选模式设置默认共享模式。然而,并没有记录下默认值是什么。 - Martijn Pieters
1
这似乎是一件奇怪的事情。很难相信Python没有锁定和共享的支持。 - David Heffernan

4
当文件以写模式打开时,其共享模式将拒绝后续的读或写操作来打开该文件。
错误。没有锁被获取。您可以从两个不同的cmd窗口尝试:
第一个窗口:
python -c "import time; f = open('tst','wb'); f.write('1'); time.sleep(3); f.write('333'); f.close"

其次,在第一个任务运行时

python -c "with open('tst','wb') as f: f.write('22')"

tst文件中的结果为1333。在第一个脚本的第一次写入后使用f.flush()刷新写操作会得到2333。丢失的数据将被替换为\x00字符,您可以通过将第一个f.write('1')替换为f.write('1'*10**6)来检查。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接