写文件操做探微

多进程读写同一个文件的问题

不考虑文件内容的错乱,多进程是能够同时读写一个文件的。当一个进程在写,读的进程可否读到最新的内容,取决于最新的内容是否真正写到了磁盘上。java

写缓存与写磁盘

先看下写文件操做的流程结构图:python

clipboard.png

磁盘缓存是物理内存的一部分,专门供操做系统用做读写磁盘的缓冲之用。磁盘缓存与“硬盘自带的缓存”是不同的概念,它的大小是能够动态设置的,而不像硬盘缓存在出厂的时候固定就是32M或64M。
咱们一般用到的写文件API,实际上是写到磁盘缓存上,可用python语言作一个实验:linux

if opt == '-w':
    with open('1.txt', 'w') as writer:
        writer.write('hehe\n')
        time.sleep(10)
elif opt == '-r':
    with open('1.txt') as fp:
        for line in fp:
            line = line.rstrip()
            print line

咱们在用-w选项写hehe以后不会马上关闭文件,而是sleep了10s,方便使用-r选项去读文件,读的时候咱们发现,除非文件关闭,不然读不出任何内容。这印证了前面的说法,hehe字符串在文件关闭前只在磁盘缓存里,还未真正写到磁盘上,因此读进程没法读出。redis

如何确保写到磁盘上而不仅是磁盘缓存里呢?python文档给出了建议:windows

file.flush() 
Flush the internal buffer.

Note
flush() does not necessarily write the file’s data to disk. Use flush() followed by os.fsync() to ensure this behavior.

文档建议咱们flush+fsync,确保内容确实更新到了磁盘。
fsync的帮助也指出了这一点:缓存

os.fsync(fd) 
Force write of file with filedescriptor fd to disk. On Unix, this calls the native fsync() function; on Windows, the MS _commit() function.

If you’re starting with a Python file object f, first do f.flush(), and then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.

所谓的“内部缓存”就是磁盘缓存,强制更新到磁盘,linux下用的是你们熟知的fsync,windows下则是_commit函数。安全

咱们将写的代码改为:异步

if opt == '-w':
    with open('1.txt', 'w') as writer:
        writer.write('hehe\n')
        writer.flush()
        os.fsync(writer.fileno())
        time.sleep(10)

果真,读进程就能在文件还没有关闭时读到hehe字符串了。
可是,fsync是要慎用的,由于每条内容都强制刷新到磁盘,虽然很是可靠,却会带来性能的急剧降低,咱们能够在上述例子的基础上改为写10万条字符串,对比“普通写”与“fsync写”的效率,会发现后者的耗时是前者的数千倍甚至是上万倍!这也正是redis的AOF日志虽然提供了fsync级别的磁盘同步却不建议咱们使用的缘由(也所以redis的日志作不到绝对的单点可靠)。函数

这里还有一个疑问,按python的文档,flush并不必定能将最新的内容更新到磁盘上,咱们查看java file API的文档,发现也有相似的说法。这是为什么?我我的的猜想,flush只是简单的把磁盘缓存的内容放到磁盘驱动程序的写请求队列里就返回,本质上是异步的,而fsync除了放内容到写请求队列还会等待磁盘驱动程序的返回结果,本质上是同步的。因为fsync还要额外经历:等待写请求到队列首部+磁盘驱动程序调用磁盘控制器+磁盘控制器写到物理磁盘等步骤,天然就拖慢了fsync的速度。性能

进程内缓存与磁盘缓存

进程内缓存指的是我在写磁盘缓存前,在本身的程序里再作一个缓存,将多条消息累积到必定的大小,再一次提交给磁盘缓存,这样能提高写的效率。java里通常要在FileWriter之上再套一层BufferedWriter写入,就是这个用途,实测下来,也能有一倍的效率提高。python语言里没有BufferedWriter,对于10万条字符串的写能够考虑别的方法,好比咱们能够每500条拼成一个大的字符串再作写入,实测也有一倍的效率提高。不过,如同前面的“普通写”与“fsync写”同样,效率的提高不是全无代价,它每每伴随着可靠性的下降。进程内缓存是属于某个进程的,一旦该进程忽然core掉,进程内缓存就会丢失,从用户层面看来,就是我明明已经write好了的数据,极可能并未写到磁盘里。相比之下,磁盘缓存就更可靠一些,由于它是由操做系统管理的,与进程无关,除非是机器断电,不然它不会丢失数据,也就是说,即便个人进程core掉,以前write的内容依然能够安全到达磁盘上。

相关文章
相关标签/搜索