J'avais un répertoire qui contenait environ 5 millions de fichiers. Lorsque j'ai essayé d'exécuter la commande ls
depuis l'intérieur de ce répertoire, mon système a consommé une énorme quantité de mémoire et s'est bloqué après un certain temps. Existe-t-il un moyen efficace de répertorier les fichiers autrement qu'en utilisant la commande ls
?
ls
trie en fait les fichiers et essaie de les lister, ce qui devient une énorme surcharge si nous essayons de lister plus d'un million de fichiers dans un répertoire. Comme mentionné dans le lien this , nous pouvons utiliser strace
ou find
pour lister les fichiers. Cependant, ces options semblaient également irréalisables à mon problème puisque j'avais 5 millions de fichiers. Après quelques recherches sur Google, j'ai constaté que si nous répertorions les répertoires à l'aide de getdents()
, cela est censé être plus rapide, car ls
, find
et Python
les bibliothèques utilisent readdir()
qui est plus lente mais utilise getdents()
en dessous.
Nous pouvons trouver le code C pour lister les fichiers en utilisant getdents()
de ici :
/*
* 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);
}
Copiez le programme C ci-dessus dans le répertoire dans lequel les fichiers doivent être répertoriés. Exécutez ensuite les commandes ci-dessous.
gcc getdents.c -o getdents
./getdents
Exemple de timing : getdents
peut être beaucoup plus rapide que ls -f
, selon la configuration du système. Voici quelques timings démontrant une augmentation de la vitesse 40x pour lister un répertoire contenant environ 500k fichiers sur un montage NFS dans un cluster de calcul. Chaque commande a été exécutée 10 fois de suite, d'abord getdents
, puis ls -f
. La première exécution est beaucoup plus lente que toutes les autres, probablement en raison de défauts de page de mise en cache NFS. (À part: sur cette monture, le d_type
champ n'est pas fiable, dans le sens où de nombreux fichiers apparaissent de type "inconnu".)
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%
Évitez de trier en utilisant:
ls --sort=none # "do not sort; list entries in directory order"
Ou équivalent:
ls -U
La raison la plus probable pour laquelle il est lent est la coloration du type de fichier, vous pouvez éviter cela avec \ls
ou /bin/ls
désactiver les options de couleur.
Si vous avez vraiment autant de fichiers dans un répertoire, utiliser find
à la place est également une bonne option.