web-dev-qa-db-fra.com

Quelqu'un peut-il expliquer une description simple concernant le «descripteur de fichier» après fork ()?

Dans "Programmation avancée dans l'environnement Unix", 2e édition, par W. Richard Stevens.

Section 8.3 fonction de fourche.

Voici la description:

Il est important que le parent et l'enfant partagent le même décalage de fichier.

Envisagez un processus qui bifurque un enfant, puis attend que l'enfant se termine. Supposons que les deux processus écrivent sur la sortie standard dans le cadre de leur traitement normal. Si le parent a sa sortie standard redirigée (par un shell, peut-être), il est essentiel que le décalage de fichier du parent soit mis à jour par l'enfant lorsque l'enfant écrit sur la sortie standard.

[1. Qu'est-ce que cela signifie? Si la sortie std du parent est redirigée vers un "fichier1" par exemple, alors que doit mettre à jour l'enfant après l'écriture de l'enfant? L'offset de sortie std d'origine du parent ou décalage redirigé de sortie (c.-à-d. file1)? Ne peut pas être le plus tard, non?]

[2. Comment la mise à jour est-elle effectuée? Par l'enfant explicitement, par le système d'exploitation implicitement, par le descripteur de fichiers lui-même? propre COPIE du descripteur de fichier. Alors, comment la mise à jour enfant est-elle décalée du côté parent?]

Dans ce cas, l'enfant peut écrire sur la sortie standard pendant que le parent l'attend; à la fin de l'enfant, le parent peut continuer à écrire sur la sortie standard, sachant que sa sortie sera ajoutée à tout ce que l'enfant a écrit. Si le parent et l'enfant ne partagent pas le même décalage de fichier, ce type d'interaction serait plus difficile à réaliser et nécessiterait des actions explicites de la part du parent.

Si le parent et l'enfant écrivent dans le même descripteur, sans aucune forme de synchronisation, telle que le fait d'attendre le parent pour l'enfant, leur sortie sera mélangée (en supposant que c'est un descripteur qui était ouvert avant le fork). Bien que cela soit possible, ce n'est pas le mode de fonctionnement normal.

Il y a deux cas normaux pour gérer les descripteurs après un fork.

  1. Le parent attend que l'enfant ait terminé. Dans ce cas, le parent n'a rien à faire avec ses descripteurs. Lorsque l'enfant se termine, l'un des descripteurs partagés à partir duquel l'enfant a lu ou écrit aura ses décalages de fichiers mis à jour en conséquence.

  2. Le parent et l'enfant suivent chacun leur propre chemin. Ici, après le fork, le parent ferme les descripteurs dont il n'a pas besoin, et l'enfant fait la même chose. De cette façon, aucun n'interfère avec les descripteurs ouverts de l'autre. Ce scénario est souvent le cas avec les serveurs réseau. "

[3. Lorsque fork () est invoqué, tout ce que je comprends, c'est que l'enfant obtient une COPIE de ce que le parent a, un descripteur de fichier dans ce cas, et fait ce qu'il veut. décalage des modifications du descripteur de fichier que le parent et l'enfant partagent, cela ne peut être que parce que le descripteur se souvient du décalage lui-même. Ai-je raison?]

Désolé, je suis un peu nouveau pour les concepts.

De l'aide? Merci.

34
user1559625

Il est important de distinguer le descripteur de fichier, qui est un petit entier que le processus utilise dans ses appels de lecture et d'écriture pour identifier le fichier, et le description du fichier, qui est une structure dans le noyau. Le décalage de fichier fait partie de la description du fichier. Il vit dans le noyau.

À titre d'exemple, utilisons ce programme:

#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(void)
{
    int fd;

    fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);

    if(!fork()) {
        /* child */
        write(fd, "hello ", 6);
        _exit(0);
    } else {
        /* parent */
        int status;

        wait(&status);
        write(fd, "world\n", 6);
    }
}

(Toute vérification d'erreur a été omise)

Si nous compilons ce programme, appelez-le hello et exécutez-le comme ceci:

./hello

voici ce qui se passe:

Le programme ouvre le fichier output, le créant s'il n'existait pas déjà ou le tronquant à zéro s'il existait. Le noyau crée une description de fichier (dans le noyau Linux, c'est un struct file) Et l'associe à un descripteur de fichier pour le processus appelant (l'entier non négatif le plus bas qui n'est pas déjà utilisé dans la table des descripteurs de fichier de ce processus) . Le descripteur de fichier est renvoyé et affecté à fd dans le programme. Par souci d'argument, supposons que fd est égal à 3.

Le programme fait un fork (). Le nouveau processus enfant obtient une copie de la table de descripteurs de fichiers de son parent, mais la description du fichier n'est pas copiée. L'entrée numéro 3 dans les tables de fichiers des deux processus pointe vers le même struct file.

Le processus parent attend pendant que le processus enfant écrit. L'écriture de l'enfant provoque le stockage de la première moitié de "hello world\n" Dans le fichier et avance le décalage de fichier de 6. Le décalage de fichier est dans le struct file!

L'enfant se ferme, la fonction wait() du parent se termine et le parent écrit, en utilisant fd 3 qui est toujours associé à la même description de fichier dont le décalage de fichier a été mis à jour par la fonction write() de l'enfant. Ainsi, la seconde moitié du message est stockée après la première partie, sans l'écraser comme elle l'aurait fait si le parent avait un décalage de fichier de zéro, ce qui serait le cas si la description du fichier n'était pas partagée.

Enfin, le parent se ferme et le noyau voit que le struct file N'est plus utilisé et le libère.

81
Alan Curry

Dans la même section, du livre, il y a un diagramme montrant trois tableaux qui sont là quand un fichier est ouvert.

La table filedescriptor de l'utilisateur (partie de l'entrée de la table de processus), la table filetable et la table inode (table v-node). Maintenant, une entrée filedescriptor (qui est un index de la table de descripteurs de fichiers) pointe vers une entrée de table de fichiers, qui pointe vers une entrée de table inode.
Maintenant, le fichier offset (la position d'où la prochaine lecture/écriture se produit) est là dans le fichier File.

Supposons donc que vous ayez un fichier ouvert dans le parent, cela signifie qu'il a également un descripteur, une entrée de table de fichiers et une référence d'inode.
Maintenant, lorsque vous créez un enfant, la table de descripteurs de fichiers est copiée pour l'enfant. Ainsi, le nombre de références dans l'entrée de table de fichiers (pour ce descripteur ouvert) est augmenté, ce qui signifie qu'il existe maintenant deux références pour la même entrée de table de fichiers.

Ce descripteur est maintenant disponible à la fois dans le parent et l'enfant, pointant vers la même entrée de la table de fichiers, partageant ainsi l'offset. Maintenant que ce contexte nous permet de voir vos questions,

  1. Qu'est-ce que ça veut dire? si la sortie std du parent est redirigée vers un "fichier1" par exemple, que doit mettre à jour l'enfant après l'écriture de l'enfant? décalage de sortie std d'origine du parent ou décalage de sortie redirigé (c'est-à-dire fichier1)? Ça ne peut pas être le plus tard, non?]

L'enfant n'a explicitement besoin de rien mettre à jour. L'auteur du livre essaie de
dites que, supposons que la sortie standard des parents soit redirigée vers un fichier et qu'un appel fork soit effectué. Après cela, le parent attend. Le descripteur est maintenant dupliqué, c'est-à-dire que l'offset du fichier est également partagé. Maintenant, chaque fois que l'enfant écrit quelque chose en standard, les données écrites sont enregistrées dans le fichier redirigé. Le décalage est automatiquement incrémenté par l'appel en écriture.

Maintenant, dites que l'enfant sort. Ainsi, le parent sort de l'attente et écrit quelque chose sur la sortie standard (qui est redirigée). Maintenant où la sortie de l'appel d'écriture du parent sera placée -> après les données, qui ont été écrites par l'enfant. Pourquoi -> puisque la valeur actuelle de l'offset est maintenant modifiée après que l'enfant a écrit.

 Parent ( )
  {
    open a file for writing, that is get the 
    descriptor( say fd);
    close(1);//Closing stdout
    dup(fd); //Now writing to stdout  means writing to the file
    close(fd)
        //Create a child that is do a  fork call.
    ret = fork();
    if ( 0 == ret )
    {
        write(1, "Child", strlen("Child");
        exit ..
    }
        wait(); //Parent waits till child exit.

         write(1, "Parent", strlen("Parent");
    exit ..
}

PL. voir le pseudo-code ci-dessus, les données finales que contient le fichier ouvert seront ChildParent. Vous voyez donc que l'offset du fichier a été modifié lorsque l'enfant a écrit et cela était disponible pour l'appel en écriture du parent, car l'offense est partagée.

2.Comment se fait la mise à jour? par enfant explicitement, par OS implicitement, par le descripteur de fichiers lui-même? Après fork, je pensais que le parent et l'enfant avaient chacun leur propre chemin et avaient leur propre COPIE du descripteur de fichier. Alors, comment la mise à jour enfant est-elle décalée du côté parent?]

Now I think the answer is clear-> by the system call that is by the OS.

[3. Lorsque fork () est invoqué, tout ce que je comprends, c'est que l'enfant obtient une COPIE de ce que le parent a, un descripteur de fichier dans ce cas, et fait son travail. Si un décalage change le descripteur de fichier que le parent et l'enfant partagent, cela ne peut être que parce que le descripteur se souvient du décalage lui-même. Ai-je raison?]

Cela devrait également être clair. L'entrée de la table de fichiers utilisateur pointe vers l'entrée de table de tables de fichiers (qui contient le décalage).

En d'autres termes, les appels système peuvent récupérer l'offset du descripteur.

4