GNU/Linux >> Tutoriels Linux >  >> Linux

Le moyen le plus efficace de copier un fichier sous Linux

Si vous savez qu'ils utiliseront un Linux> 2.6.17, splice() est le moyen de faire une copie zéro sous Linux :

 //using some default parameters for clarity below. Don't do this in production.
 #define splice(a, b, c) splice(a, 0, b, 0, c, 0)
 int p[2];
 pipe(p);
 int out = open(OUTFILE, O_WRONLY);
 int in = open(INFILE, O_RDONLY)
 while(splice(p[0], out, splice(in, p[1], 4096))>0);

Malheureusement, vous ne pouvez pas utiliser sendfile() ici car la destination n'est pas un socket. (Le nom sendfile() vient de send() + "fichier").

Pour zéro copie, vous pouvez utiliser splice() comme suggéré par @Dave. (Sauf qu'il ne s'agira pas d'une copie zéro ; ce sera "une copie" du cache de page du fichier source vers le cache de page du fichier de destination.)

Cependant... (a) splice() est spécifique à Linux ; et (b) vous pouvez presque certainement faire aussi bien en utilisant des interfaces portables, à condition de les utiliser correctement.

En bref, utilisez open() + read() + write() avec un petit tampon temporaire. Je suggère 8K. Votre code ressemblerait donc à ceci :

int in_fd = open("source", O_RDONLY);
assert(in_fd >= 0);
int out_fd = open("dest", O_WRONLY);
assert(out_fd >= 0);
char buf[8192];

while (1) {
    ssize_t read_result = read(in_fd, &buf[0], sizeof(buf));
    if (!read_result) break;
    assert(read_result > 0);
    ssize_t write_result = write(out_fd, &buf[0], read_result);
    assert(write_result == read_result);
}

Avec cette boucle, vous copierez 8 Ko du cache de page in_fd dans le cache CPU L1, puis l'écrivez du cache L1 dans le cache de page out_fd. Ensuite, vous écraserez cette partie du cache L1 avec le prochain morceau de 8K du fichier, et ainsi de suite. Le résultat net est que les données en buf ne sera jamais réellement stocké dans la mémoire principale (sauf peut-être une fois à la fin) ; du point de vue de la RAM du système, c'est aussi bien que d'utiliser "zero-copy" splice() . De plus, il est parfaitement portable sur n'importe quel système POSIX.

Notez que le petit tampon est la clé ici. Les processeurs modernes typiques ont environ 32 Ko pour le cache de données L1, donc si vous rendez le tampon trop grand, cette approche sera plus lente. Peut-être beaucoup, beaucoup plus lent. Gardez donc le tampon dans la plage "quelques kilo-octets".

Bien sûr, à moins que votre sous-système de disque ne soit très très rapide, la bande passante mémoire n'est probablement pas votre facteur limitant. Je recommanderais donc posix_fadvise pour que le noyau sache ce que vous faites :

posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);

Cela indiquera au noyau Linux que sa machinerie de lecture anticipée devrait être très agressive.

Je suggérerais également d'utiliser posix_fallocate pour préallouer l'espace de stockage pour le fichier de destination. Cela vous indiquera à l'avance si vous manquerez de disque. Et pour un noyau moderne avec un système de fichiers moderne (comme XFS), cela aidera à réduire la fragmentation dans le fichier de destination.

La dernière chose que je recommanderais est mmap . C'est généralement l'approche la plus lente de toutes grâce à la raclée TLB. (Des noyaux très récents avec des "pages énormes transparentes" pourraient atténuer cela ; je n'ai pas essayé récemment. Mais c'était certainement très mauvais avant. Je ne prendrais donc la peine de tester que mmap si vous avez beaucoup de temps pour comparer et un noyau très récent.)

[Mise à jour]

Il y a une question dans les commentaires à savoir si splice d'un fichier à l'autre est zéro-copie. Les développeurs du noyau Linux appellent cela "le vol de page". La page de manuel pour splice et les commentaires dans la source du noyau indiquent que le SPLICE_F_MOVE flag devrait fournir cette fonctionnalité.

Malheureusement, le support de SPLICE_F_MOVE a été retiré en 2.6.21 (en 2007) et jamais remplacé. (Les commentaires dans les sources du noyau n'ont jamais été mis à jour.) Si vous recherchez les sources du noyau, vous trouverez SPLICE_F_MOVE n'est en fait référencé nulle part. Le dernier message que je peux trouver (de 2008) dit qu'il est "en attente d'un remplacement".

L'essentiel est que splice d'un fichier à un autre appelle memcpy déplacer les données ; ce n'est pas zéro-copie. Ce n'est pas beaucoup mieux que ce que vous pouvez faire dans l'espace utilisateur en utilisant read /write avec de petits tampons, alors autant s'en tenir aux interfaces standard et portables.

Si le "vol de page" est rajouté dans le noyau Linux, alors les avantages de splice serait beaucoup plus grand. (Et même aujourd'hui, lorsque la destination est un socket, vous obtenez une vraie copie zéro, ce qui fait splice plus attrayant.) Mais pour les besoins de cette question, splice ne vous achète pas beaucoup.


Linux
  1. Copier des fichiers dans le terminal Linux

  2. Un moyen facile de masquer des fichiers et des répertoires sous Linux

  3. gcp - Un utilitaire avancé de copie de fichiers pour Linux

  4. Obtenir le fichier le plus récent dans un répertoire sous Linux

  5. moyen le plus rapide de convertir un fichier délimité par des tabulations en csv sous Linux

Comment copier un fichier dans plusieurs répertoires sous Linux

Comment copier des fichiers et des répertoires sous Linux

Commande Cp sous Linux (Copier des fichiers)

Copier un fichier dans plusieurs répertoires à partir de la ligne de commande sous Linux

Comment copier des fichiers et des répertoires dans un terminal Linux

Fichier de copie Linux - Linux CP Made Easy!