我有一些大致像这样的 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)。(此时这将取决于磁盘硬件。)
因此,看起来,当进程退出时,其标准输出流被刷新,但如果该流实际上由指向磁盘上文件的文件描述符支持,则不能保证写入磁盘已完成。我怀疑这可能就是这里发生的事情。
所以,我的实际问题:
我对规格的阅读是否正确?在其重定向的标准输出在磁盘上可用之前,子进程是否可以在其父进程看来已终止?
是否有可能以某种方式等待子进程写入文件的所有数据实际上已由操作系统同步到磁盘?
我应该在父进程的文件对象副本上调用
flush()
还是某个 Python 版本?fsync()
这可以强制子进程写入相同的文件描述符以提交到磁盘吗?