mv 1970-01-01 00:00

背景

最近一个项目,需要限制日志文件的大小,所以会起一个线程去检测日志文件的大小,当到达限制大小后,拷贝日志文件,然后将日志文件清空。

代码大概长这样子:

   // 写日志
   f = open("log.file")
   log(f, "hello,world")
   .....

   // 其他线程监控日志文件大小

   size = f.stat().size()
   if size > maxSize{
        CopyFileContent("log.file", "log.file.bak")
        ftruncate(f.fd(), 0)
        f.lseek(0, 0)
   }

然后有同学问,为什么不直接用mv?

MV

我们先了解一下mv怎么实现的。

mv source target

我们知道当调用unlink删除文件时,如果有进程正在访问那个文件,内核就不会实际删除文件,所以常常利用这个特性来创建临时文件。

当处于第一种情况时,日志还是会往target上面写,并且你可以通过tail -f来实时查看日志输出, 当处于第二种情况时,你通过ls已经找不到日志文件了,但是文件本身(inode)还是存在的,进程还是在往文件里面写。

验证

用下面的程序验证第一种情况:

mport os
import time

with open("test.txt", "ab") as f:
    i = 1
    while 1:
        f.write("%s\n"%(i,))
        f.flush()
        time.sleep(1)
        i += 1

程序运行时,mv test.txt test.txt2,你可以看到后续的输出被追加到了test.txt2上面。

验证第二种情况:

import os
import time
import os

f = open("test.txt", "w+")

if os.fork() > 0:
    i = 1
    while 1:
        f.write("%s\n"%(i,))
        f.flush()
        if i == 4:
                os.system("mv test.txt /tmp/test.txt")
                break
        time.sleep(1)
        i += 1
else:
    time.sleep(5)
    f.seek(0)
    for line in f.readlines():
        print line,

保证程序的当前目录和/tmp不在同一个文件系统下面。

你会发现虽然父进程虽然调用了mv命令,但是子进程在睡眠唤醒后,还是会输出1234,也就是说文件本身并没有被删除。