2017-05-17 1 views
4

J'expérimente avec du code C pour l'implémentation de shell et fgets trouvé() retourne des lignes dupliquées quand après je lance un processus, que je ne pouvais pas comprendre, et j'apprécierais grandement toute aide .La lecture d'une entrée à l'aide de fgets retourne des lignes dupliquées dans C

Ma question est: le forking change-t-il l'offset dans tous les fichiers ouverts dans le processus parent? Cela semble se produire dans mon programme.

DE LA réponse ci-dessous @Vadim Ponomarev et ma compréhension: fgets() n'est pas thread-safe (ou à proprement parler, il est, mais bifurquer un processus provoque l'stdin à initialiser en quelque sorte, ce qui le changement du décalage de fichier partagé).

Le code va comme ceci:

int main() { 

    char buf[200]; 
    int r; 
    pid_t pid = 0; 

    while(getcmd(buf, 200, pid) >= 0) { 
    fprintf(stderr, "current pid: %d\n", getpid()); 
    pid = fork(); 
    // Without forking the fgets() reads all lines normally 
    if(pid == 0) 
     exit(0); 

    wait(&r); 
    } 

    return 0; 
} 

La fonction getcmd() est juste un emballage:

int 
getcmd(char *buf, int nbuf, pid_t pid) 
{ 
    memset(buf, 0, nbuf); 
    if (fgets(buf, nbuf, stdin) == NULL) { 
    fprintf(stderr, "EOF !!!\n"); 
    return -1; 
    } 
    fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf); 
    return 0; 
} 

J'ai aussi un fichier d'entrée temp avec quelques textes aléatoires:

line 1 
line 2 
line 3 

Après la compilation, et je cours a.out < temp, la sortie indique que 6 lignes sont imprimées et généralement certaines lignes sont dupliquées. Mais si je supprime la ligne

pid = fork() 
... 

alors la sortie devient normal (juste montrer toutes les lignes, un par un, ce qui signifie fgets() est appelé 3 fois).

Une idée de ce qui ne va pas?

sortie (ce que a):

pid: 10361 -- getcmd buf ======= --> line1 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line2 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line3 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line2 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line3 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line3 

current pid: 10361 
EOF !!! 

Et je me attends à ceci:

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line1 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line2 

current pid: 10361 
pid: 10361 -- getcmd buf ======= --> line3 

EOF 

Une version compilable pour référence:

#include <stdio.h> 
#include <stdlib.h> 
#include <wait.h> 
#include <zconf.h> 
#include <unistd.h> 
#include <memory.h> 

int 
getcmd(char *buf, int nbuf, pid_t pid) 
{ 
    memset(buf, 0, nbuf); 
    if (fgets(buf, nbuf, stdin) == NULL) { 
    fprintf(stderr, "EOF !!!\n"); 
    return -1; 
    } 
    fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf); 
    return 0; 
} 

int main() { 

    char buf[200]; 
    int r; 
    pid_t pid = 0; 

    while(getcmd(buf, 200, pid) >= 0) { 
    fprintf(stderr, "current pid: %d\n", getpid()); 
    pid = fork(); 
    // Without forking the fgets() reads all lines normally 
    if(pid == 0) 
     exit(0); 

    wait(&r); 
    } 

    return 0; 
} 

Merci!

+0

Pouvez-vous s'il vous plaît modifier votre question pour * montrer * la sortie réelle (et attendue) (en entier, copier-coller comme texte)? Veuillez également inclure un exemple réel [minimal, complet et vérifiable] (http://stackoverflow.com/help/mcve), quelque chose que nous pouvons facilement copier et tester nous-mêmes. –

+0

@Someprogrammerdude Bonjour, j'ai ajouté les sorties. En un mot, je ne m'attendais pas à voir des lignes en double lues. –

+0

parent et enfant partagent le même descripteur de fichier, qu'est-ce que vous attendez dans la fourchette sur le stdin? –

Répondre

4
  1. il a été déjà mentionné que parents et enfants partagent la position actuelle pour descripteur de fichier 0 (stdin)
  2. semble que l'initialisation d'exécution libc pour les flux (stdin, stdout, stderr) contient quelque chose de changement de position de stdin actuelle :

    > strace -f ./a.out < temp 2>&1 | less 
    .... 
    write(2, "pid: 29487 -- getcmd buf ======="..., 45pid: 29487 -- getcmd buf ======= --> line 1 
    clone(child_stack=0,flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,child_tidptr=0x7f34940f19d0) = 29488 
    Process 29488 attached 
    [pid 29487] wait4(-1, <unfinished ...> 
    [pid 29488] lseek(0, -14, SEEK_CUR)  = 7 
    [pid 29488] exit_group(0)    = ? 
    [pid 29488] +++ exited with 0 +++ 
    <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29488 
    

s'il vous plaît noter lseek (0, -14, SEEK_CUR) chez l'enfant (pid 29488)

  1. en conséquence, dans mon environnement (openSUSE Leap 42.2, glibc-2.22-4.3.1) le programme boucle indéfiniment et il n'y a pas EOF à tous

  2. fgets changé() pour lire() dans l'exemple

    .... 
    if (read(0, buf, nbuf) == 0) { 
    .... 
    while(getcmd(buf, 7, pid) >= 0) { 
    .... 
    

et programme se déroule comme prévu (trois lignes et EOF)

  1. et exécutez strace -f à nouveau - pas plus lseek() dans l'enfant !!

  2. Conclusion - semble que les fonctions de flux (déclarées dans stdio.h) doit être utilisé avec une grande prudence dans un environnement multi-processus en raison de nombreux effets secondaires (comme dans cet exemple)

+0

Merci pour la bonne réponse! –

+0

En fait, j'ai aussi essayé d'utiliser pos = lseek (0, 0, SEEK_CUR) pour obtenir le décalage actuel avant de forcer, et lseek (0, pos, SEEK_SET) pour réinitialiser la position du fichier après wait(). Il s'est également avéré fonctionner. –