【问题标题】:why system call unlink so slow?为什么系统调用 unlink 这么慢?
【发布时间】:2015-10-05 13:45:42
【问题描述】:
#include <unistd.h>
#include <stdio.h>

void dump_log(int size){
    char cmd[1024];
    snprintf(cmd, sizeof(cmd)/sizeof(cmd[0]), "dd if=/dev/zero of=from.bin bs=1024 count=%d", size);
    int ret = system(cmd);
    if (ret<0){
        perror("system");
    }

}

int main(){    
    const char *filepath = "from.bin";

    while(1){
        dump_log(1024*100);
        sleep(10);
        unlink(filepath);
    }

    return 0;
}

strace -T ./a.out 显示这个:

unlink("from.bin")                      = 0 <0.019916>

unlink a file(100M) 耗时 19ms,unlink 文件时会发生什么?为何这么慢?

系统信息: linux 3.13.0-57-generic,Ubuntu 14.04.2 LTS,ext4

【问题讨论】:

标签: linux performance disk unlink ext4


【解决方案1】:

如果您有一个大文件要unlink(2),内核不会解锁 inode,直到所有块指针都返回到空闲块列表。您可以通过创建第二个链接来检查时间差异(这将使取消链接仅释放您正在删除的链接,而不释放任何块)。根据规范,释放所有这些块的代码是您的进程(好吧,在内核模式下运行,而不是用户模式,但没有保留进程将块返回到空闲列表)并且在释放所有块之前它不会返回。

示例:(编辑)

以下代码将说明这一点:

#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define D(X) "%s:%d:%s: " X, __FILE__, __LINE__, __func__

int main(int argc, char **argv)
{
    int opt, i;

    while ((opt = getopt(argc, argv, "")) != EOF) {
        switch (opt) {
        } /* switch */
    } /* while */

    argc -= optind; argv += optind;

    for (i = 0; i < argc; i++) {
        struct timespec now, then; 
        int res;

        res = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
        if (res < 0) {
            fprintf(stderr,
                    D("ERROR: %s (errno = %d)\n"),
                    strerror(errno), errno);
            exit(EXIT_FAILURE);
        } /* if */

        unlink(argv[i]);

        res = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &then);
        if (res < 0) {
            fprintf(stderr, D("ERROR: %s (errno = %d)\n"),
                    strerror(errno), errno);
            exit(EXIT_FAILURE);
        } /* if */
        then.tv_nsec -= now.tv_nsec;
        then.tv_sec -= now.tv_sec;
        if (then.tv_nsec < 0) {
            then.tv_nsec += 1000000000L;
            then.tv_sec--;
        } /* if */
        printf(D("%s: %d.%09d s. (CPU time)\n"),
                argv[i], then.tv_sec, then.tv_nsec);
    } /* for */
    exit(EXIT_SUCCESS);
} /* main */

然后我用这个命令构造一个 2Gb 的文件:

$ yes | dd of=pepe bs=1M iflag=fullblock count=2048

然后我创建了 32 个指向该文件的链接:

i=0
while [ "$i" -lt 32 ]
do ln pepe pepe$i
   i=$(expr $i + 1)
done

然后我运行以下命令(仅显示 CPU 时间):

$ unlink pepe[0-9]* pepe
unlink.c:47:main: pepe0: 0.000074272 s. (CPU time)
unlink.c:47:main: pepe1: 0.000022722 s. (CPU time)
unlink.c:47:main: pepe10: 0.000015034 s. (CPU time)
unlink.c:47:main: pepe11: 0.000013254 s. (CPU time)
unlink.c:47:main: pepe12: 0.000012827 s. (CPU time)
unlink.c:47:main: pepe13: 0.000012462 s. (CPU time)
unlink.c:47:main: pepe14: 0.000012241 s. (CPU time)
unlink.c:47:main: pepe15: 0.000012753 s. (CPU time)
unlink.c:47:main: pepe16: 0.000012517 s. (CPU time)
unlink.c:47:main: pepe17: 0.000012245 s. (CPU time)
unlink.c:47:main: pepe18: 0.000013104 s. (CPU time)
unlink.c:47:main: pepe19: 0.000012491 s. (CPU time)
unlink.c:47:main: pepe2: 0.000012662 s. (CPU time)
unlink.c:47:main: pepe20: 0.000012606 s. (CPU time)
unlink.c:47:main: pepe21: 0.000012803 s. (CPU time)
unlink.c:47:main: pepe22: 0.000012597 s. (CPU time)
unlink.c:47:main: pepe23: 0.000012391 s. (CPU time)
unlink.c:47:main: pepe24: 0.000012582 s. (CPU time)
unlink.c:47:main: pepe25: 0.000012557 s. (CPU time)
unlink.c:47:main: pepe26: 0.000012386 s. (CPU time)
unlink.c:47:main: pepe27: 0.000012261 s. (CPU time)
unlink.c:47:main: pepe28: 0.000012245 s. (CPU time)
unlink.c:47:main: pepe29: 0.000012351 s. (CPU time)
unlink.c:47:main: pepe3: 0.000011940 s. (CPU time)
unlink.c:47:main: pepe30: 0.000013003 s. (CPU time)
unlink.c:47:main: pepe31: 0.000012231 s. (CPU time)
unlink.c:47:main: pepe4: 0.000012777 s. (CPU time)
unlink.c:47:main: pepe5: 0.000012546 s. (CPU time)
unlink.c:47:main: pepe6: 0.000012461 s. (CPU time)
unlink.c:47:main: pepe7: 0.000013129 s. (CPU time)
unlink.c:47:main: pepe8: 0.000012311 s. (CPU time)
unlink.c:47:main: pepe9: 0.000012446 s. (CPU time)
unlink.c:47:main: pepe: 0.195457587 s. (CPU time)

如您所见,除最后一个链接外,所有链接都需要大约 12 微秒,但最后一个链接几乎是十分之二秒的执行时间。

【讨论】:

  • 至少我们现在有了基于扩展的文件系统,比如 ext4(启用扩展)、XFS 以及几乎所有比 ext2 更新的文件系统。在 ext2 中,有一个实际的块位图,并且必须列出文件使用的每个块。现在,inode 使用哪些块的信息存储得更加紧凑,如 start+length 范围。在extents 之前,大文件通常花费大量时间到rm(1)。 (我使用 XFS,即使对于大文件(除非非常碎片),即使在旋转媒体上,取消链接也很快。)
  • @PeterCordes,我不完全明白你的意思。我试图在一个四核、8GB 内存和 ext4 文件系统上说明这种现象。至少我认为所有 unlink(2) 和最后一个之间的区别足以说明除了执行 unlink(2) 的过程之外什么都没有系统调用来安排文件中的所有块返回到空闲列表(无论它在文件系统中采用什么形式)
  • 我的意思是,在我们拥有擅长处理大文件的文件系统之前,大文件的情况会更糟。删除最后一个链接将比其他任何链接都慢,因为它们只是减少引用计数,但它是毫秒,而不是秒。 (你的 0.2s 时间太长了。你的 ext4 FS 挂载时是否启用了 extents?)
  • ops...抱歉,我误会了你 :) 我不知道我的范围是否处于活动状态。我进行了另一项测试,以擦除一个孔文件(使用dd if=/dev/zero of=pepe bs=1k count=1 seek=128000000 制作的文件)并仅占用磁盘中的一个块,结果相同。
  • @PeterCordes extents enabled 如果是 ext4,如果是 ext3 则禁用,但挂载为 ext4。我说的对吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-03
  • 2018-11-10
  • 2014-09-28
相关资源
最近更新 更多