La fermeture d’un tuyau est-elle nécessaire lorsqu’elle est suivie de execlp ()?

Avant d’énoncer ma question, j’ai lu plusieurs questions connexes sur le dépassement de capacité de la stack, telles que les fonctions pipe & dup dans UNIX et plusieurs autres, mais n’a pas clarifié ma confusion.

Tout d’abord, le code, qui est un exemple de code tiré de ‘Beginning Linux Programming’, 4e édition, chapitre 13:

#include  #include  #include  #include  int main() { int data_processed; int file_pipes[2]; const char some_data[] = "123"; pid_t fork_result; if (pipe(file_pipes) == 0) { fork_result = fork(); if (fork_result == (pid_t)-1) { fprintf(stderr, "Fork failure"); exit(EXIT_FAILURE); } if (fork_result == (pid_t)0) // Child process { close(0); dup(file_pipes[0]); close(file_pipes[0]); // LINE A close(file_pipes[1]); // LINE B execlp("od", "od", "-c", (char *)0); exit(EXIT_FAILURE); } else // parent process { close(file_pipes[0]); // LINE C data_processed = write(file_pipes[1], some_data, strlen(some_data)); close(file_pipes[1]); // LINE D printf("%d - wrote %d bytes\n", (int)getpid(), data_processed); } } exit(EXIT_SUCCESS); } 

Le résultat de l’exécution est:

momo @ xue5: ~ / TestCode / IPC_Pipe $ ./a.out

10187 – écrit 3 octets

momo @ xue5: ~ / TestCode / IPC_Pipe $ 0000000 1 2 3

0000003

momo @ xue5: ~ / TestCode / IPC_Pipe $

Si vous avez commenté LINE A, LINE C et LINE D , le résultat est le même que ci-dessus. Je comprends le résultat. L’enfant récupère les données de son parent via son propre stdin qui est connecté au canal et envoie le résultat “od -c” à sa sortie standard.

Cependant, si vous commentiez LINE B , le résultat serait:

momo @ xue5: ~ / TestCode / IPC_Pipe $ ./a.out

10436 – écrit 3 octets

momo @ xue5: ~ / TestCode / IPC_Pipe $

Pas de résultat ‘od -c’! Est-ce que ‘od -c’ démarré par execlp () n’est pas excuté, ou sa sortie n’est pas dirigée vers stdout? Une possibilité est que le read () de ‘od’ soit bloqué, car le descripteur de fichier en écriture file_pipes [1] de l’enfant est ouvert si vous avez commenté LINE B. Mais le commentaire en LIGNE D, qui permet d’écrire le descripteur de fichier file_pipes [1] du parent ouvert , peut toujours avoir la sortie ‘od -c’.

Et pourquoi avons-nous besoin de fermer le tuyau avant execlp ()? execlp () remplacera l’image de processus, y compris la stack, .data, .heap, .text par la nouvelle image de ‘od’. Est-ce que cela signifie que même si vous ne fermez pas file_pipes [0] et file_pipes [1] dans les enfants tels que LINE A et B, file_pipes [0] et file_pipes [1] seront toujours “détruits” par execlp ()? Du résultat par code, ce n’est pas. Mais où est-ce que je me trompe?

Merci beaucoup pour votre temps et vos efforts ici ~ ~

La fermeture d’un tuyau est-elle nécessaire lorsqu’elle est suivie de execlp ()?

Ce n’est pas ssortingctement nécessaire car cela dépend de l’utilisation du tuyau. Mais en général, oui, il devrait être fermé si l’extrémité du pipe n’est pas nécessaire au processus.

Pourquoi avons-nous besoin de fermer le tube avant execlp ()? execlp () remplacera l’image du processus

Parce que les descripteurs de fichier (par défaut) restnt ouverts pour tous les appels exec . A partir de la page de manuel : “Par défaut, les descripteurs de fichier restnt ouverts avec un execve (). Les descripteurs de fichier marqués close-on-exec sont fermés; voir la description de FD_CLOEXEC dans fcntl (2).”

Cependant, si vous avez commenté LINE B, … Aucun résultat ‘od -c’!

Cela est dû au fait que le processus od lit à partir de stdin jusqu’à ce qu’il obtienne un EOF . Si le processus lui-même ne ferme pas file_pipes[1] il ne verra pas un EOF car le bout d’écriture en écriture ne serait pas complètement fermé par tous les processus qui l’avaient ouvert.

Si vous avez commenté la ligne A, la ligne C et la ligne D, il est le même que ci-dessus

En effet, les descripteurs de fichier situés en A et en C sont des extrémités de lecture du canal et personne ne sera bloqué dans l’attente de sa fermeture (comme décrit ci-dessus). Le descripteur de fichier en D est une fin d’écriture et ne pas le fermer causerait effectivement des problèmes. Cependant, même si le code n’appelle pas explicitement close sur ce descripteur de fichier, celui-ci sera toujours fermé car le processus se termine.

Et pourquoi avons-nous besoin de fermer le tuyau avant execlp ()? execlp () remplacera l’image de processus, y compris la stack, .data, .heap, .text par la nouvelle image de ‘od’.

Oui, les fonctions exec-family, y compris execlp() , remplacent l’image de processus du processus appelant par une copie du programme spécifié. Mais la table des descripteurs de fichiers ouverts du processus ne fait pas partie de l’image du processus. Elle est gérée par le kernel et survit à l’exéc.

Est-ce que cela signifie que même si vous ne fermez pas file_pipes [0] et file_pipes [1] dans les enfants tels que LINE A et B, file_pipes [0] et file_pipes [1] seront toujours “détruits” par execlp ()?

La variable file_pipes est détruite par execlp() , mais il ne s’agit que de la mémoire interne du programme pour les descripteurs de fichier. Les descripteurs ne sont que des index entiers dans une table gérée par le kernel pour le processus. Si vous perdez la trace des valeurs du descripteur de fichier, les fichiers associés ne seront pas fermés. En fait, c’est une forme de fuite de ressources.

Du résultat par code, ce n’est pas. Mais où est-ce que je me trompe?

Comme décrit ci-dessus.

En outre, lorsqu’un processus se termine, tous ses descripteurs de fichier ouverts sont fermés, mais la description de fichier ouverte sous-jacente dans le kernel, à laquelle les descripteurs de fichier font référence, n’est fermée que lorsqu’il ne rest aucun descripteur de fichier ouvert s’y rapportant. Des descripteurs de fichier ouverts supplémentaires peuvent être conservés par d’autres processus, en conséquence de leur inheritance via un fork() .

Maintenant, en ce qui concerne la question spécifique de ce qui se passe lorsque le processus enfant ne ferme pas file_pipes[1] avant d’exécuter od , vous pouvez obtenir un indice en vérifiant la liste des processus via la commande ps . Vous verrez que le processus enfant est toujours en cours d’exécution (peut-être plusieurs, si vous avez testé plusieurs fois). Pourquoi?

Eh bien, comment peut-il savoir quand sortir? Il traite l’ intégralité de ses entrées. Il doit donc sortir lorsqu’il atteint la fin de ses entrées. Mais la fin de la saisie sur un canal ne signifie pas qu’aucune autre donnée n’est disponible pour le moment , car il est possible que d’autres données soient écrites ultérieurement à la fin du tube. La fin de l’entrée sur un canal se produit lorsque la fin de l’écriture est fermée . Et si l’enfant ne ferme pas file_pipes[1] avant de s’exécuter, il restra probablement ouvert indéfiniment, car après l’exécuteur, l’enfant ne sait plus qu’il en est le propriétaire.