dup2 / dup - pourquoi aurais-je besoin de dupliquer un descripteur de fichier?

85

J'essaie de comprendre l'utilisation de dup2et dup.

Depuis la page de manuel:

DESCRIPTION

dup and dup2 create a copy of the file descriptor oldfd.
After successful return of dup or dup2, the old and new descriptors may
be used interchangeably. They share locks, file position pointers and
flags; for example, if the file position is modified by using lseek on
one of the descriptors, the position is also changed for the other.

The two descriptors do not share the close-on-exec flag, however.

dup uses the lowest-numbered unused descriptor for the new descriptor.

dup2 makes newfd be the copy of oldfd, closing newfd first if necessary.  

RETURN VALUE

dup and dup2 return the new descriptor, or -1 if an error occurred 
(in which case, errno is set appropriately).  

Pourquoi aurais-je besoin de cet appel système? à quoi sert de dupliquer le descripteur de fichier?

Si j'ai le descripteur de fichier, pourquoi voudrais-je en faire une copie?

J'apprécierais si vous pouviez m'expliquer et me donner un exemple où dup2/ dupest nécessaire.

Merci

JAN
la source
Comment implémenteriez-vous la fonctionnalité de tuyauterie des coques sans dupou dup2? Vous devez appeler pipe(2)et ensuite avoir l'un des descripteurs de fichierdupSTDIN_FILENO
Basile Starynkevitch
1
Dup ou dup2
DrTyrsa

Réponses:

46

L'appel système dup duplique un descripteur de fichier existant, en renvoyant un nouveau qui fait référence au même objet d'E / S sous-jacent.

Dup permet aux shells d'implémenter des commandes comme celle-ci:

ls existing-file non-existing-file > tmp1  2>&1

Le 2> & 1 indique au shell de donner à la commande un descripteur de fichier 2 qui est un double du descripteur 1. (ie stderr & stdout pointent vers le même fd).
Maintenant, le message d'erreur pour appeler ls sur un fichier non existant et la sortie correcte de ls sur le fichier existant apparaissent dans le fichier tmp1 .

L'exemple de code suivant exécute le programme wc avec une entrée standard connectée à l'extrémité de lecture d'un tube.

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
    close(STDIN); //CHILD CLOSING stdin
    dup(p[STDIN]); // copies the fd of read end of pipe into its fd i.e 0 (STDIN)
    close(p[STDIN]);
    close(p[STDOUT]);
    exec("/bin/wc", argv);
} else {
    write(p[STDOUT], "hello world\n", 12);
    close(p[STDIN]);
    close(p[STDOUT]);
}

L'enfant copie la fin de lecture sur le descripteur de fichier 0, ferme le fichier de scriptors dans p et exécute wc. Lorsque wc lit depuis son entrée standard, il lit depuis le tube.
C'est ainsi que les tubes sont implémentés en utilisant dup, eh bien qu'une utilisation de dup maintenant vous utilisez pipe pour construire autre chose, c'est la beauté des appels système, vous construisez une chose après l'autre en utilisant des outils qui sont déjà là, ces outils ont été à leur tour construits en utilisant autre chose et ainsi de suite .. A la fin les appels système sont les outils les plus basiques que vous obtenez dans le noyau

À votre santé :)

Pensée profonde
la source
1
dupEst-ce donc utile pour l'appelant et non pour le lsprogramme lui-même? Y a-t-il un avantage à avoir duputilisé dans un programme comme ls lui-même s'il a déjà accès au fichier? Ici, par exemple, lsécrit des erreurs dans 2lesquelles est codé en dur, j'ai donc un moyen de le contourner en tant que consommateur de ls. Je pense que c'est un point subtil non?
Nishant
2
Votre programme d'exemple semble avoir un bogue; vous appelez dup(p[STDIN])mais jetez ensuite le résultat. Vouliez-vous utiliser dup2(p[STDIN], 0)?
Quuxplusone
1
@Quuxplusone duprenvoie le "descripteur numéroté le plus petit actuellement non utilisé par le processus." Puisque fd 0 vient juste d'être fermé, dupdevrait renvoyer 0. dup2est explicite sur quel fd devrait être utilisé, au lieu d'utiliser simplement le fd libre le plus bas, donc je préférerais cela.
Wodin
@Wodin: Ah, je parie que vous avez raison sur ce que pensait OP. Ai-je également raison, cependant, que "juste fermé" est relatif et que le code d'OP pourrait se rompre en présence, par exemple, de threads simultanés qui pourraient également ouvrir des fichiers?
Quuxplusone
@Quuxplusone Je suppose que vous avez raison, mais je ne sais pas avec certitude. Mais dans ce cas, vous aurez d'autres problèmes. Si vous fermez stdin parce que vous voulez lire ailleurs et qu'un autre thread ouvre un fichier avant vous, il obtiendra fd 0. Si vous utilisez ensuite dup2, il fermera le fd que l'autre thread a ouvert, donc l'autre thread va maintenant lire (et écrire?) le fichier que vous ouvrez. Quoi qu'il en soit, si je me souviens bien, vous ne devriez pas appeler à exec*partir d'un processus multithread. Mais je ne suis pas un expert en threading :)
Wodin
18

Une autre raison de dupliquer un descripteur de fichier est de l'utiliser avec fdopen. fcloseferme le descripteur de fichier qui a été transmis fdopen, donc si vous ne voulez pas que le descripteur de fichier d'origine soit fermé, vous devez d'abord le dupliquer dup.

R .. GitHub STOP AIDING ICE
la source
fdopen()semble ne pas dupliquer un descripteur de fichier, il crée simplement un tampon dans l'espace utilisateur.
Eric Wang
3
Vous avez mal lu ma réponse. Le fait est que vous voudrez peut-être duple fd avant de le passer fdopencar fclosele fermera.
R .. GitHub STOP AIDER ICE
1
@ theferrit32: Si vous allouez un FILEhandle pour accéder à un fichier ouvert préexistant via les interfaces stdio, vous devez appeler fclosepour désallouer ce FILEhandle. Si vous voulez continuer à utiliser le fichier ouvert sous-jacent, ou si votre architecture logicielle est telle que le code «propriétaire» original du descripteur de fichier le fera close, le fait que cela fcloseferme également le descripteur de fichier sous-jacent que vous avez remis fdopenest un problème. Vous pouvez éviter ce problème en utilisant duppour créer un nouveau descripteur de fichier pour le même fichier ouvert à transmettre fdopen, afin que fclosene ferme pas l'original.
R .. GitHub STOP HELPING ICE
1
Le fait est que fdopen () déplace la propriété du fd vers le FILE, plutôt que de le copier . C'est quelque chose dont les utilisateurs doivent être conscients. Les consommateurs qui doivent conserver une fdpoignée utilisable en plus de l' FILEobjet doivent dupliquer le fichier fd. C'est tout.
Conrad Meyer
1
@ConradMeyer: Oui, c'est une très bonne façon de le dire, avec une note qu'il n'y a pas d'opération pour "déplacer la propriété" de la FILEfois que vous en avez transféré la propriété.
R .. GitHub STOP HELPING ICE
4

dup permet de rediriger la sortie d'un processus.

Par exemple, si vous souhaitez enregistrer la sortie d'un processus, vous dupliquez la sortie (fd = 1), vous redirigez le fd dupliqué vers un fichier, puis forkez et exécutez le processus, et lorsque le processus se termine, vous redirigez le fichier sauvé fd en sortie.

Alinsoar
la source
4

Certains points liés à dup / dup2 peuvent être notés s'il vous plaît

dup / dup2 - Techniquement, le but est de partager une entrée de table de fichiers dans un même processus par différentes poignées. (Si nous forçons, le descripteur est dupliqué par défaut dans le processus enfant et l'entrée de la table de fichiers est également partagée).

Cela signifie que nous pouvons avoir plus d'un descripteur de fichier ayant éventuellement des attributs différents pour une seule entrée de table de fichier ouverte en utilisant la fonction dup / dup2.

(Bien qu'il semble actuellement que seul l'indicateur FD_CLOEXEC soit le seul attribut d'un descripteur de fichier).

http://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html

dup(fd) is equivalent to fcntl(fd, F_DUPFD, 0);

dup2(fildes, fildes2); is equivalent to 

   close(fildes2);
   fcntl(fildes, F_DUPFD, fildes2);

Les différences sont (pour le dernier) - Mis à part une certaine valeur errno entre dup2 et fcntl close suivie de fcntl peut soulever des conditions de concurrence puisque deux appels de fonction sont impliqués.

Les détails peuvent être vérifiés sur http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html

Un exemple d'utilisation -

Un exemple intéressant lors de l'implémentation du contrôle de travail dans un shell, où l'utilisation de dup / dup2 peut être vue ..dans le lien ci-dessous

http://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html#Launching-Jobs

Tanmoy Bandyopadhyay
la source