권한을 표시하는 %M 옵션이 있는 것과 없는 것 사이의 엄청난 성능 차이

권한을 표시하는 %M 옵션이 있는 것과 없는 것 사이의 엄청난 성능 차이

CentOS 7.6에서 다음을 실행하여 3,000,000개의 파일이 포함된 폴더(Many_files라고 함)를 만들었습니다.

for i in {1..3000000}; do echo $i>$i; done;

이 명령을 사용하여 find해당 디렉터리의 파일 정보를 파일에 씁니다. 이것은 놀랍도록 빠르게 작동합니다.

$ time find many_files -printf '%i %y %p\n'>info_file

real    0m6.970s
user    0m3.812s
sys     0m0.904s

%M이제 권한을 얻기 위해 추가하면 다음과 같습니다 .

$ time find many_files -printf '%i %y %M %p\n'>info_file

real    2m30.677s
user    0m5.148s
sys     0m37.338s

이 명령은 시간이 더 오래 걸립니다. C 프로그램에서는 파일의 inode와 권한 정보를 얻을 수 있고 이 정보를 struct stat커널에 저장할 수 있기 때문에 이것이 나를 놀라게 했습니다.struct inode

내 질문:

  1. 이 동작의 원인은 무엇입니까?
  2. 너무 많은 파일에 대한 파일 권한을 얻는 더 빠른 방법이 있습니까?

답변1

첫 번째 버전에는 다음 사항만 필요합니다.readdir(3)/getdents(2)이 기능을 지원하는 파일 시스템에서 실행될 때의 디렉터리입니다(ext4: filetype기능은 으로 표시 tune2fs -l /dev/xxx, xfs: ... ftype=1로 표시 xfs_info /mount/point).

두 번째 버전도 있습니다반품필요stat(2)파일별로 추가 inode 조회가 필요하므로 파일 시스템과 장치에 대한 더 많은 조회가 필요합니다. 이는 회전하는 디스크이고 캐시가 유지되지 않는 경우 상당히 느려질 수 있습니다. stat이름, inode 및 파일 유형만 찾는 경우에는 디렉토리 항목이면 충분하므로 이는 필요하지 않습니다.

  The linux_dirent structure is declared as follows:

       struct linux_dirent {
           unsigned long  d_ino;     /* Inode number */
           unsigned long  d_off;     /* Offset to next linux_dirent */
           unsigned short d_reclen;  /* Length of this linux_dirent */
           char           d_name[];  /* Filename (null-terminated) */
                             /* length is actually (d_reclen - 2 -
                                offsetof(struct linux_dirent, d_name)) */
           /*
           char           pad;       // Zero padding byte
           char           d_type;    // File type (only since Linux
                                     // 2.6.4); offset is (d_reclen - 1)
           */
       }

다음과 같은 경우에도 동일한 정보를 사용할 수 있습니다 readdir(3).

struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported
                                   by all filesystem types */
    char           d_name[256]; /* Null-terminated filename */
};

의심되었지만 (더 작은 샘플에서...) 다음 두 출력을 비교하여 확인되었습니다.

strace -o v1 find many_files -printf '%i %y %p\n'>info_file
strace -o v2 find many_files -printf '%i %y %M %p\n'>info_file

내 Linux amd64 커널 5.0.x에서는 이것이 주요 차이점으로 나타납니다.

[...]

 getdents(4, /* 0 entries */, 32768)     = 0
 close(4)                                = 0
 fcntl(5, F_DUPFD_CLOEXEC, 0)            = 4
-write(1, "25499894 d many_files\n25502410 f"..., 4096) = 4096
-write(1, "iles/844\n25502253 f many_files/8"..., 4096) = 4096
-write(1, "096 f many_files/686\n25502095 f "..., 4096) = 4096
-write(1, "es/529\n25501938 f many_files/528"..., 4096) = 4096
-write(1, "1 f many_files/371\n25501780 f ma"..., 4096) = 4096
-write(1, "/214\n25497527 f many_files/213\n2"..., 4096) = 4096
-brk(0x55b29a933000)                     = 0x55b29a933000
+newfstatat(5, "1000", {st_mode=S_IFREG|0644, st_size=5, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "999", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "998", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "997", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "996", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "995", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "994", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "993", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "992", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "991", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+newfstatat(5, "990", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0

[...]

+newfstatat(5, "891", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
+write(1, "25499894 d drwxr-xr-x many_files"..., 4096) = 4096
+newfstatat(5, "890", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0

[...]

관련 정보