【问题标题】:On Linux, is access() faster than stat()?在 Linux 上,access() 是否比 stat() 快?
【发布时间】:2015-12-21 07:11:51
【问题描述】:

我会假设 access() 只是 stat() 的包装器,但我一直在谷歌搜索,并有 found some anecdotes 关于用“更便宜”的访问调用替换 stat 调用。假设您只对检查文件是否存在感兴趣,那么访问速度会更快吗?它是否因文件系统而完全不同?

【问题讨论】:

  • 它似乎是 open() 的包装。对我来说,它会更快,而且它具有 setuid 意识,这确实很有意义。不过,我希望看到一些基准。
  • access 在 man 第 2 节中,所以我假设它是它自己的系统调用,而不是围绕某些东西的 libc 包装器(因此,理论上,它可能比 stat 等更快)
  • 重要的不是性能,而是语义。 access() 告诉您即使在涉及任何访问控制插件(即 SELinux)或其他组件(扩展 ACL;AFS 权限等)之后,您也可以读/写/执行事物。 stat() 告诉您传统的 UNIX 文件权限,但它们可能是也可能不是全部。而且因为access() [可能] 正在做更多的工作,我实际上预计它会更慢。
  • 我想它非常依赖。相关示例:在我的 PC 上,fstat()pread() 快 2 倍。然而,在一个带有 MIPS cpu 的低 RAM 机器上,fstat()pread() 慢 1.5 倍 。因此,此评论的信息是您应该始终测量以获取您的环境的具体时间。

标签: linux performance file posix stat


【解决方案1】:

理论

我对此表示怀疑。

在内核的较低层中,access()stat() 调用之间没有太大区别,它们都执行 查找操作:它们将文件名映射到 dentry 缓存中的条目 em> 和 inode(它是实际的内核结构,inode)。查找操作很慢,因为您需要对路径的每个部分执行它,即对于/usr/bin/cat,您需要查找usrbin,然后是cat,它可能需要从磁盘读取——这就是为什么inode 和 dentries 缓存在内存中。

这两个调用之间的主要区别在于stat()inode 结构转换为stat 结构,而access() 将进行简单的检查,但与查找 时间。

真正的性能提升可以通过 at 来实现 - 像 faccessat()fstatat() 这样的操作,它们允许 open() 目录一次,只需比较:

struct stat s;
stat("/usr/bin/cat", &s);   // lookups usr, bin and cat = 3
stat("/usr/bin/less", &s);  // lookups usr, bin and less = 3

int fd = open("/usr/bin");  // lookups usr, bin = 2
fstatat(fd, "cat", &s);     // lookups cat = 1
fstatat(fd, "less", &s);    // lookups less = 1

实验

我写了一个小python脚本,调用stat()access()

import os, time, random
files = ['gzexe', 'catchsegv', 'gtroff', 'gencat', 'neqn', 'gzip', 
        'getent', 'sdiff', 'zcat', 'iconv', 'not_exists', 'ldd', 
        'unxz', 'zcmp', 'locale', 'xz', 'zdiff', 'localedef', 'xzcat']
access = lambda fn: os.access(fn, os.R_OK)

for i in xrange(1, 80000): 
    try:
        random.choice((access, os.stat))("/usr/bin/" + random.choice(files))
    except:
        continue

我使用 SystemTap 跟踪系统以测量在不同操作中花费的时间。 stat()access() 系统调用都使用user_path_at_empty() 内核函数,代表查找操作:

stap -ve ' global tm, times, path;
probe lookup = kernel.function("user_path_at_empty") 
    { name = "lookup"; pathname = user_string_quoted($name); }
probe lookup.return = kernel.function("user_path_at_empty").return 
    { name = "lookup"; }
probe stat = syscall.stat 
    { pathname = filename; }
probe stat, syscall.access, lookup
        { if(pid() == target() && isinstr(pathname, "/usr/bin")) {
        tm[name] = local_clock_ns(); } }
probe syscall.stat.return, syscall.access.return, lookup.return
        { if(pid() == target() && tm[name]) {
        times[name] <<< local_clock_ns() - tm[name];
        delete tm[name];
        } }
    ' -c 'python stat-access.py'

结果如下:

         COUNT      AVG
lookup   80018    1.67 us
stat     40106    3.92 us
access   39903    4.27 us

请注意,我在实验中禁用了 SELinux,因为它会对结果产生重大影响。

【讨论】:

  • 您正在测量缓存的案例。 stat 可能需要检查一些 access 不需要的东西,比如块数。大多数文件系统可能会在 inode 中存储块计数,而不必遍历块映射 B-tree,因为 dir 中的 stat-every-file 是他们需要优化的非常常见的操作。鉴于此,回答stat 查询所需的一切可能都来自于读取 inode 的磁盘寻道。
  • 请不要盲目相信这个建议。我在我的机器上测试了一下,stat 和 faccessat 的性能似乎完全一样。
  • @Siscia 你是如何进行实验的?没有更多信息,很难说是什么因素起作用。
猜你喜欢
  • 1970-01-01
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
  • 1970-01-01
  • 2012-06-13
  • 2011-03-27
相关资源
最近更新 更多