不刷新到磁盘大文件的调用结束后,随即()?(Large file not flushed to di

2019-08-03 15:58发布

我创建大文件和我的Python脚本(超过1GB ,居然还有人8)。 紧接着我创造他们我要创建过程中,将使用这些文件。

该脚本的样子:

# This is more complex function, but it basically does this:
def use_file():
    subprocess.call(['C:\\use_file', 'C:\\foo.txt']);


f = open( 'C:\\foo.txt', 'wb')
for i in 10000:
    f.write( one_MB_chunk)
f.flush()
os.fsync( f.fileno())
f.close()

time.sleep(5) # With this line added it just works fine

t = threading.Thread( target=use_file)
t.start()

但是,应用程序use_file的作用就像foo.txt是空的。 有一些奇怪的事情怎么回事:

  • 如果我执行C:\use_file C:\foo.txt控制台(脚本结束后)我得到正确的结果
  • 如果我手动执行use_file()在另一个Python控制台我得到正确的结果
  • C:\foo.txt是上盘之后可见open()被调用,但仍尺寸0B直到脚本结束
  • 如果我添加time.sleep(5)它只是开始按预期工作(或者说必须)

我已经发现:

  • os.fsync()但它似乎没有工作(从结果use_file仿佛是C:\foo.txt是空的)
  • 使用buffering=(1<<20)开口文件时)似乎没有任何工作

我对这种行为越来越好奇。

问题:

  • 难道蟒蛇 close()操作为背景? 这哪里是记录?
  • 如何来解决这个身边?
  • 我缺少的东西吗?
  • 加入后sleep :是一个Windows / Python的错误吗?

注:(对于有些不对劲与对方的情况下)应用程序use_data用途:

handle = CreateFile("foo.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
                               OPEN_EXISTING, 0, NULL);
size = GetFileSize(handle, NULL)

然后处理size从字节foo.txt

Answer 1:

f.close()调用f.flush()将数据发送到OS。 这并不一定是数据写入磁盘,因为操作系统对其进行缓冲。 当你正确地制定出来,如果要强制操作系统将其写入到磁盘,您需要os.fsync()

你有没有考虑干脆直接通过管道将数据转换成use_file


编辑:你说os.fsync() “不工作”。 为了澄清,如果你这样做

f = open(...)
# write data to f
f.flush()
os.fsync(f.fileno())
f.close()

import pdb; pdb.set_trace()

再看看磁盘上的文件,它有数据吗?



Answer 2:

编辑:具体到Python 3.x的信息更新

有一个超级老bug报告在讨论一个suspiciosly类似的问题https://bugs.python.org/issue4944 。 我做了一个小测试,显示错误: https://gist.github.com/estyrke/c2f5d88156dcffadbf38

在错误链接入门用户从一个eryksun解释精彩上面之后,我现在明白为什么会这样,是不是本身的错误每。 在Windows创建一个子进程,默认情况下它继承了所有打开的文件从父进程句柄。 所以你看到的可能是真正共享冲突,因为你想在子进程读取该文件是开放的,通过继承的手柄在另一个子进程写。 事件的可能的序列导致该(使用在高于主旨的再现为例):

Thread 1 opens file 1 for writing
  Thread 2 opens file 2 for writing
  Thread 2 closes file 2
  Thread 2 launches child 2
  -> Inherits the file handle from file 1, still open with write access
Thread 1 closes file 1
Thread 1 launches child 1
-> Now it can't open file 1, because the handle is still open in child 2
Child 2 exits
-> Last handle to file 1 closed
Child 1 exits

当我编译简单的C子程序和我的机器上运行脚本,它失败在大多数使用Python 2.7.8时的线程中的至少一个。 与Python 3.2和3.3不重定向测试脚本不会失败,因为默认值close_fds参数subprocess.call现在True当不使用重定向。 其他使用测试脚本重定向仍然未能在这些版本中。 在Python 3.4两种测试成功,因为PEP 446,这使得所有的文件处理非继承默认的。

结论

从Python中的线程产卵一个子进程意味着孩子将继承所有打开的文件句柄,甚至从其他线程不是一个地方的孩子产生了。 这是,至少对我来说,不是特别直观。

可能的解决方案:

  • 升级到Python 3.4,其中文件句柄是默认情况下不继承。
  • 通过close_fds=Truesubprocess.call到完全禁用继承(这是在Python 3.x的默认值)。 不过,请注意,这阻止了子进程的标准输入/输出/错误的重定向。
  • 确保所有文件产生新的进程之前被关闭。
  • 使用os.open与打开文件os.O_NOINHERIT在Windows标志。
    • tempfile.mkstemp也使用此标志。
  • 使用WIN32API来代替。 传递一个NULL指针为lpSecurityAttributes参数也防止继承描述符:

     from contextlib import contextmanager import win32file @contextmanager def winfile(filename): try: h = win32file.CreateFile(filename, win32file.GENERIC_WRITE, 0, None, win32file.CREATE_ALWAYS, 0, 0) yield h finally: win32file.CloseHandle(h) with winfile(tempfilename) as infile: win32file.WriteFile(infile, data) 


文章来源: Large file not flushed to disk immediately after calling close()?