web-dev-qa-db-ja.com

パーミッションを表示するために%Mオプションを使用した場合と使用しない場合のコマンドfindの大きなパフォーマンスの違い

私の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プログラムではstruct statを使用してファイルのiノードとパーミッション情報を取得でき、カーネルではstruct inodeが両方の情報を保存するので、これは私にとって非常に驚くべきことです。

私の質問:

  1. この動作の原因は何ですか?
  2. 非常に多くのファイルのファイル権限を取得するより速い方法はありますか?
7
Bahram

最初のバージョンでは、 readdir(3) / getdents(2) をディレクトリで実行すると、この機能をサポートするファイルシステム(ext4:_tune2fs -l /dev/xxx_で表示されるfiletype機能、xfs:_ftype=1_で表示される_xfs_info /mount/point_ ...)。

さらに2番目のバージョンまたには stat(2) が必要であり、追加のiノードルックアップ、つまりファイルシステムとデバイスでのシークの増加。回転ディスクであり、キャッシュが保持されていない場合は、おそらくかなり遅くなります。ディレクトリエントリで十分であるため、名前、iノード、ファイルタイプのみを検索する場合、このstatは必要ありません。

_  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 */
};
_

疑わしいが、以下の2つの出力を(小さいサンプルで)比較することによって確認された:

_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
_

[...]

12
A.B