1

我有一些大致像这样的 Python 代码,使用一些您可能拥有或没有的库:

# Open it for writing
vcf_file = open(local_filename, "w")

# Download the region to the file.
subprocess.check_call(["bcftools", "view",
    options.truth_url.format(sample_name), "-r",
    "{}:{}-{}".format(ref_name, ref_start, ref_end)], stdout=vcf_file)

# Close parent process's copy of the file object
vcf_file.close()

# Upload it
file_id = job.fileStore.writeGlobalFile(local_filename)

基本上,我正在启动一个子进程,该子进程应该为我下载一些数据并将其打印到标准输出。我将该数据重定向到一个文件,然后,一旦子进程调用返回,我将关闭该文件的句柄,然后将该文件复制到其他地方。

我观察到,有时,我期望的数据的尾部没有进入副本。现在,可能bcftools只是偶尔不写入该数据,但我担心我可能会做一些不安全的事情,并且在subprocess.check_call()返回后以某种方式访问​​文件,但在子进程写入标准输出的数据之前放到我可以看到的磁盘上。

查看 C 标准(因为 bcftools 是用 C/C++ 实现的),看起来当程序正常退出时,所有打开的流(包括标准输出)都被刷新和关闭。请参阅此处[lib.support.start.term]的部分,描述 的行为,它在返回时被隐式调用:exit()main()

--接下来,所有打开的带有未写入缓冲数据的 C 流(由 中声明的函数签名介导)被刷新,所有打开的 C 流都被关闭,所有通过调用 tmp-file() 创建的文件都被删除。30)

--最后,控制权返回到宿主环境。如果状态为零或 EXIT_SUCCESS,则返回状态成功终止的实现定义形式。如果状态为 EXIT_FAILURE,则返回状态不成功终止的实现定义形式。否则返回的状态是 implementation-defined.31)

因此,在子进程退出之前,它会关闭(并因此刷新)标准输出。

但是,Linux的手册页close(2)指出,关闭文件描述符并不一定保证写入它的任何数据都已实际写入磁盘:

成功关闭并不能保证数据已成功保存到磁盘,因为内核延迟写入。文件系统在流关闭时刷新缓冲区并不常见。如果您需要确保数据是物理存储的,请使用 fsync(2)。(此时这将取决于磁盘硬件。)

因此,看起来,当进程退出时,其标准输出流被刷新,但如果该流实际上由指向磁盘上文件的文件描述符支持,则不能保证写入磁盘已完成。我怀疑这可能就是这里发生的事情。

所以,我的实际问题:

  1. 我对规格的阅读是否正确?在其重定向的标准输出在磁盘上可用之前,子进程是否可以在其父进程看来已终止?

  2. 是否有可能以某种方式等待子进程写入文件的所有数据实际上已由操作系统同步到磁盘?

  3. 我应该在父进程的文件对象副本上调用flush()还是某个 Python 版本?fsync()这可以强制子进程写入相同的文件描述符以提交到磁盘吗?

4

1 回答 1

1

是的,在数据​​写入磁盘(物理上)之前可能需要几分钟。但是你可以在那之前很久就读到它。

除非您担心电源故障或内核崩溃;数据是否在磁盘上并不重要。内核是否认为数据已写入的重要部分。

返回后立即从文件中读取是安全的check_call()。如果您没有看到所有数据;它可能表示存在错误bcftools或未writeGlobalFile()上传文件中的所有数据。bsftools您可以尝试通过禁用'stdout 的块缓冲模式(提供伪 tty、使用unbuffer命令行实用程序等)来解决前者。

问:我对规格的解读是否正确?在其重定向的标准输出在磁盘上可用之前,子进程是否可以在其父进程看来已终止?

是的。是的。

问:是否有可能以某种方式等到子进程写入文件的所有数据实际上已被操作系统同步到磁盘?

不。fsync()在一般情况下是不够的。很可能,无论如何您都不需要它(读回数据是一个不同的问题,与确保将其写入磁盘不同)。

问:我应该在父进程的文件对象副本上调用 flush() 还是某些 Python 版本的 fsync()?这可以强制子进程写入相同的文件描述符以提交到磁盘吗?

这将毫无意义。.flush()刷新父进程内部的缓冲区(您可以使用它open(filename, 'wb', 0)来避免在父进程中创建不必要的缓冲区)。

fsync()在文件描述符上工作(孩子有自己的文件描述符)。我不知道内核是否对引用同一磁盘文件的不同文件描述符使用不同的缓冲区。同样,没关系 - 如果您观察到数据丢失(无崩溃);fsync()在这里帮不上忙。

问:为了清楚起见,我看到您断言数据确实应该由其他进程读取,因为相关的操作系统缓冲区在进程之间共享。但是你的断言的来源是什么?您可以在规范或 Linux 文档中指出保证这些缓冲区是共享的吗?

查找write()成功返回常规文件后”

read()从文件中被该写入修改的每个字节位置的任何成功都应返回由该write() 位置指定的数据,直到再次修改此类字节位置。

于 2016-01-06T05:35:30.033 回答