EXT4上的时间戳精度问题

4
更新:事实证明我非常愚蠢。我应该检查访问时间而不是修改时间。之所以无法重现,是因为测试文件是使用dd if=/dev/urandom of="$target" bs='1K' count=1 || exit 1创建的,大多数情况下这个过程太快了,新文件的修改时间(dd的结束时间)和访问时间(dd的开始时间)是相同的。还有一件事需要注意。

我正在编写一个脚本,将一个文件的访问时间加上两年,并应用到另一个文件上。这使用stat -c %xdate --rfc-3339=nstouch -a --date="$result"statdate都输出带有纳秒的日期字符串,例如

2012-11-17 10:22:15.390351800+01:00

根据 info coreutils 'touch invocation',touch 命令支持纳秒级别的时间戳。但有时候使用 touch 命令时,应用的时间戳与之后由 stat 返回的时间戳之间存在微小差异。以下是实际运行数据:

$ for i in {1..100}; do ./t_timecopy.sh 2>/dev/null| grep ASSERT; done
ASSERT:Expecting same access time expected:<2012-11-17 10:58:40.719320935+01:00> but was:<2012-11-17 10:58:40.723322203+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:00:04.342346275+01:00> but was:<2012-11-17 11:00:04.346358718+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:00:39.343348183+01:00> but was:<2012-11-17 11:00:39.347351686+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:01:08.655348312+01:00> but was:<2012-11-17 11:01:08.659347625+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:01:37.930346876+01:00> but was:<2012-11-17 11:01:37.934347311+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:02:16.939319832+01:00> but was:<2012-11-17 11:02:16.943323061+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:02:46.456443149+01:00> but was:<2012-11-17 11:02:46.458379114+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:03:15.487339595+01:00> but was:<2012-11-17 11:03:15.491341426+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:04:04.646335863+01:00> but was:<2012-11-17 11:04:04.650346634+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:04:14.410326608+01:00> but was:<2012-11-17 11:04:14.414331233+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:04:24.159367348+01:00> but was:<2012-11-17 11:04:24.163352418+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:04:33.931387953+01:00> but was:<2012-11-17 11:04:33.935350115+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:05:03.394361030+01:00> but was:<2012-11-17 11:05:03.398320957+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:05:42.054317430+01:00> but was:<2012-11-17 11:05:42.059106497+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:06:40.346320820+01:00> but was:<2012-11-17 11:06:40.350346956+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:08:17.194346778+01:00> but was:<2012-11-17 11:08:17.198338832+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:08:27.102347603+01:00> but was:<2012-11-17 11:08:27.106320380+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:09:16.247322948+01:00> but was:<2012-11-17 11:09:16.251347966+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:09:55.191325266+01:00> but was:<2012-11-17 11:09:55.195320672+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:12:09.915318301+01:00> but was:<2012-11-17 11:12:09.919334099+01:00>
ASSERT:Expecting same access time expected:<2012-11-17 11:12:28.906346914+01:00> but was:<2012-11-17 11:12:28.910348186+01:00>

有21个测试中有失败了,平均值为3.938毫秒,中位数为4.001毫秒。有什么想法是什么原因导致这种情况发生?

$ uname -a
Linux user 2.6.32-22-generic #33-Ubuntu SMP Wed Apr 28 13:27:30 UTC 2010 i686 GNU/Linux

如果您提供正在使用的测试脚本的代码,这将有助于我们。此外,在您执行此操作时,您确定没有任何内容读取(注意:atime)这些文件吗? - thkala
这会解释清楚了 :-). 还有两件事情你要记住:1. 尽管文件系统支持纳秒级分辨率,但这并不意味着内核实际上使用这种分辨率来计时。 2. 访问时间是敏感的。你需要使用strictatime挂载选项显式地启用它们,这将明显影响文件系统的性能(通常默认为relatime)。它们还容易受到任何能够访问你所监视文件的随机应用程序的攻击。 - thkala
2个回答

0

我使用了这一堆(虽然有点草率)的单行代码在我的系统上测试了你的问题 - 一个Mandriva Linux 2010.1(x86-64):

seq 1 1000 | while read f; do sleep 0.01; touch test-$f-0; done

seq 1 1000 | while read f; do touch -a -d "$(stat -c %x test-$f-0 | sed 's|^2010|2012|')" test-$f-1; done

seq 1 1000 | while read f; do A="$(stat -c %x test-$f-0)"; B="$(stat -c %x test-$f-1)"; if [[ ! "${A#2010}" = "${B#2012}" ]]; then echo test-$f; fi; done

我无法重现您的问题。听起来,触摸未在-d参数处提供预期的时间戳,而是计算出其他内容。

当然,问题可能是特定于系统的,如果是这种情况,我们需要更多有关您的系统的信息(CPU、操作系统是32位还是64位、内核/glibc/coreutils版本等)。

更新:

我尝试了使用stat和touch的32位版本。没有遇到任何问题。内核仍然是64位的。

更新2:

我还尝试了一组关注atime的oneliner:

$ seq 1 1000 | while read f; do sleep 0.01; touch test-$f-0; done
$ seq 1 1000 | while read f; do sleep 0.01; touch test-$f-1; done
$ seq 1 1000 | while read f; do sleep 0.01; cat test-$f-0; done
$ seq 1 1000 | while read f; do touch -a -d "$(stat -c %x test-$f-0 | sed 's|^2010|2012|')" test-$f-1; done
$ seq 1 1000 | while read f; do A="$(stat -c %x test-$f-0)"; B="$(stat -c %x test-$f-1)"; if [[ ! "${A#2010}" = "${B#2012}" ]]; then echo test-$f; fi; done

再次没有发现问题。我尝试了使用relatime和strictatime挂载选项。

更新3:

我刚刚在我的Mandriva i686笔记本上执行了上述测试。我似乎也没有遇到纳秒精度的问题。我还在另一个32位系统上进行了验证,如果不支持纳秒精度(例如在ext3上),则stat输出中的纳秒字段变为零。


即使文件系统四舍五入时间,你的测试也无法捕捉到它,因为测试中唯一的时间来源是通过stat(1)获取的文件系统本身。 - unbeli
根据问题,OP想要“将一个文件的访问时间加上两年应用于另一个文件”。他已经在使用FS作为参考。 - thkala
@unbeli,OP似乎遇到的问题是touch -a -d“whenever”不能将“whenever”准确设置为文件的atime。实际上,“whenever”时间来自哪里并不重要。 - thkala
你使用的是EXT4吗?大多数文件系统只保存秒级别的时间戳,而不是纳秒级别的。 - l0b0
@thkala,这很相关。如果文件系统截断了时间,你无法通过提供已经截断的时间来检测到它。来吧,动动你的脑筋! - unbeli
显示剩余2条评论

0

在64位的Windows 7上触控存在类似的问题。这是我的漏洞利用代码:

touch a && touch b && ls --full-time a b
touch -r a b && ls --full-time a b

输出结果为:

-rw-rw-rw-  1 Jarek 0 0 2012-05-09 12:05:27.851839700 +0200 a
-rw-rw-rw-  1 Jarek 0 0 2012-05-09 12:05:27.874841000 +0200 b

-rw-rw-rw-  1 Jarek 0 0 2012-05-09 12:05:27.851839700 +0200 a
-rw-rw-rw-  1 Jarek 0 0 2012-05-09 12:05:27.851839000 +0200 b

lstouch来自gnuwin32。在前两个输出行中,时间戳差异为20毫秒。很好。但在第二次运行中,它们应该相等(ba中取得了时间戳)。没有运气。有0.7微秒的差异:)。

svn status看到了这个差异,因此很难用touch欺骗它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接