2009-10-16 15 views
7

Voici une autre question à propos de splice(). J'espère l'employer pour copier des dossiers, et essaye d'employer deux appels d'épissure joints par un tuyau comme l'exemple sur la page de Wikipedia de épissure. J'ai écrit un exemple simple de test qui essaie de ne lire que les premiers 32K octets d'un fichier et de les écrire à un autre:Comment utiliser la fonction splice() de Linux pour copier un fichier dans un autre fichier?

#define _GNU_SOURCE 
#include <fcntl.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 

int main(int argc, char **argv) { 
    int pipefd[2]; 
    int result; 
    FILE *in_file; 
    FILE *out_file; 

    result = pipe(pipefd); 

    in_file = fopen(argv[1], "rb"); 
    out_file = fopen(argv[2], "wb"); 

    result = splice(fileno(in_file), 0, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE); 
    printf("%d\n", result); 

    result = splice(pipefd[0], NULL, fileno(out_file), 0, 32768, SPLICE_F_MORE | SPLICE_F_MOVE); 
    printf("%d\n", result); 

    if (result == -1) 
     printf("%d - %s\n", errno, strerror(errno)); 

    close(pipefd[0]); 
    close(pipefd[1]); 
    fclose(in_file); 
    fclose(out_file); 

    return 0; 
} 

Quand je lance cela, le fichier d'entrée semble être lu correctement, mais le second appel d'épissure échoue avec EINVAL. Quelqu'un sait ce que je fais mal ici?

Merci!

+0

Pour quiconque lisant ceci, le deuxième appel 'splice' devrait seulement essayer de lire le nombre d'octets du tuyau comme le premier appel' splice' renvoyé. Sur les Linux de nos jours, la taille du tube par défaut est '65535'. – Jite

Répondre

3

De quel (s) système (s) de fichiers copiez-vous? Votre exemple fonctionne sur mon système lorsque les deux fichiers sont sur ext3 mais échoue lorsque j'utilise un lecteur externe (j'oublie désinvolte si c'est DOS ou NTFS). Ma conjecture est que l'un ou les deux de vos fichiers sont sur un système de fichiers que l'épissure ne supporte pas.

+0

NTFS aurait du sens - cela est implémenté via FUSE, et le pilote du système de fichiers fonctionne comme un processus d'espace utilisateur. Avec d'autres systèmes de fichiers, otoh, le système de fichiers peut être utilisé directement avec le cache de page. Il est dommage que splice() n'ait pas de repli automatique propre à une boucle de copie ... – bdonlan

+0

C'est NTFS bien que splice ne semble pas fonctionner sur DOS non plus. D'accord sur le repli. Je suis tenté de faire quelques repères simples car il semble impressionnant à l'œil nu. – Duck

4

De l'splice manpage:

EINVAL Target file system doesn't support splicing; target file is 
      opened in append mode; neither of the descriptors refers to a 
      pipe; or offset given for non-seekable device. 

Nous savons l'un des descripteurs est un tuyau, et le fichier n'est pas ouvert en mode append. Nous savons également qu'aucun offset n'est donné (0 est équivalent à NULL - vouliez-vous dire passer un pointeur sur un décalage d'origine?), Donc ce n'est pas le problème. Par conséquent, le système de fichiers que vous utilisez ne prend pas en charge l'épissage des fichiers.

+0

C'était le problème. Merci! J'aurais dû lire la page de manuel tout au long, et je n'avais pas réalisé que l'épissure dépendait du système de fichiers. Dans mon cas, je copiais sur un fichier NFS. J'essaie de trouver le moyen le plus rapide de copier de nombreux fichiers (d'environ 10 Mo en moyenne) d'un système de fichiers NFS à un autre. mmap-ing le fichier source et en utilisant write() ne sont pas spectaculaires. Merci pour les pointeurs! –

2

Le splice(2) system call est destiné à la copie entre fichiers et canaux et non entre fichiers. Il ne peut donc pas être utilisé pour copier entre des fichiers, comme l'ont indiqué les autres réponses.

Depuis Linux 4.5, un nouveau copy_file_range(2) system call est disponible et permet de copier entre les fichiers. Dans le cas de NFS, il peut même provoquer une copie côté serveur.

La page de manuel liée contient un exemple complet de programme.