it-swarm.com.de

Der Befehl ls funktioniert nicht für ein Verzeichnis mit einer großen Anzahl von Dateien

Ich hatte ein Verzeichnis mit ungefähr 5 Millionen Dateien. Als ich versuchte, den Befehl ls in diesem Verzeichnis auszuführen, verbrauchte mein System sehr viel Speicher und blieb nach einiger Zeit hängen. Gibt es eine effiziente Möglichkeit, die Dateien anders als mit dem Befehl ls aufzulisten?

73
Ramesh

ls sortiert die Dateien tatsächlich und versucht, sie aufzulisten, was zu einem enormen Aufwand wird, wenn wir versuchen, mehr als eine Million Dateien in einem Verzeichnis aufzulisten. Wie in this link erwähnt, können wir strace oder find verwenden, um die Dateien aufzulisten. Diese Optionen schienen jedoch auch für mein Problem nicht durchführbar zu sein, da ich 5 Millionen Dateien hatte. Nach einigem googeln stellte ich fest, dass wenn wir die Verzeichnisse mit getdents() auflisten, es schneller sein soll, weil ls, find und Python Bibliotheken verwenden readdir(), das langsamer ist, aber getdents() darunter verwendet.

Wir können den C-Code finden, um die Dateien mit getdents() von hier aufzulisten:

/*
 * List directories using getdents() because ls, find and Python libraries
 * use readdir() which is slower (but uses getdents() underneath.
 *
 * Compile with 
 * ]$ gcc  getdents.c -o getdents
 */
#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct linux_dirent {
   long           d_ino;
   off_t          d_off;
   unsigned short d_reclen;
   char           d_name[];
};

#define BUF_SIZE 1024*1024*5

int
main(int argc, char *argv[])
{
   int fd, nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   int bpos;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for ( ; ; ) {
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       for (bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           d_type = *(buf + bpos + d->d_reclen - 1);
           if( d->d_ino != 0 && d_type == DT_REG ) {
              printf("%s\n", (char *)d->d_name );
           }
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

Kopieren Sie das obige C-Programm in das Verzeichnis, in dem die Dateien aufgelistet werden müssen. Führen Sie dann die folgenden Befehle aus.

gcc  getdents.c -o getdents
./getdents

Timings-Beispiel : getdents kann viel schneller sein als ls -f, abhängig von der Systemkonfiguration. Im Folgenden finden Sie einige Zeitangaben, die eine 40-fache Geschwindigkeitssteigerung für die Auflistung eines Verzeichnisses mit etwa 500.000 Dateien über einen NFS-Mount in einem Computercluster demonstrieren. Jeder Befehl wurde 10 Mal unmittelbar hintereinander ausgeführt, zuerst getdents, dann ls -f. Der erste Durchlauf ist erheblich langsamer als alle anderen, wahrscheinlich aufgrund von NFS-Caching-Seitenfehlern. (Nebenbei: über diesem Reittier das d_type Feld ist unzuverlässig in dem Sinne, dass viele Dateien als "unbekannter" Typ angezeigt werden.)

command: getdents $bigdir
usr:0.08 sys:0.96  wall:280.79 CPU:0%
usr:0.06 sys:0.18  wall:0.25   CPU:97%
usr:0.05 sys:0.16  wall:0.21   CPU:99%
usr:0.04 sys:0.18  wall:0.23   CPU:98%
usr:0.05 sys:0.20  wall:0.26   CPU:99%
usr:0.04 sys:0.18  wall:0.22   CPU:99%
usr:0.04 sys:0.17  wall:0.22   CPU:99%
usr:0.04 sys:0.20  wall:0.25   CPU:99%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
command: /bin/ls -f $bigdir
usr:0.53 sys:8.39  wall:8.97   CPU:99%
usr:0.53 sys:7.65  wall:8.20   CPU:99%
usr:0.44 sys:7.91  wall:8.36   CPU:99%
usr:0.50 sys:8.00  wall:8.51   CPU:100%
usr:0.41 sys:7.73  wall:8.15   CPU:99%
usr:0.47 sys:8.84  wall:9.32   CPU:99%
usr:0.57 sys:9.78  wall:10.36  CPU:99%
usr:0.53 sys:10.75 wall:11.29  CPU:99%
usr:0.46 sys:8.76  wall:9.25   CPU:99%
usr:0.50 sys:8.58  wall:9.13   CPU:99%
49
Ramesh

Vermeiden Sie das Sortieren mit:

ls --sort=none # "do not sort; list entries in directory order"

Oder äquivalent:

ls -U
71
Hauke Laging

Der wahrscheinlichste Grund, warum es langsam ist, ist das Färben von Dateitypen. Sie können dies vermeiden, indem Sie \ls Oder /bin/ls Die Farboptionen deaktivieren.

Wenn Sie wirklich so viele Dateien in einem Verzeichnis haben, ist die Verwendung von find ebenfalls eine gute Option.

12
Alex Lehmann