2010-12-09 43 views
1

J'ai des problèmes avec fork() et ce genre de choses.Fork(): Ne pas revenir de l'enfant jusqu'à ce qu'il soit terminé

Je développe un shell, où l'utilisateur peut écrire des commandes qui seront exécutées comme dans un shell normal et commun.

J'ai une fonction principale comme ceci:

void Shell::init() { 
    string command; 
    while (1) { 
     cout << getPrompt() << " "; 
     command = readCommand(); 
     if (command.length() > 0) handleCommand(command); 
    } 
} 

handleCommand() est la fonction qui fait à peu près tout. Quelque part dans, je donne les résultats suivants:

... 
else { 
    pid_t pid; 
    pid = fork(); 

    char* arg[tokens.size() + 1]; 
    for (int i = 0; i < tokens.size(); ++i) { 
     arg[i] = (char*) tokens[i].c_str(); 
    } 
    arg[tokens.size()] = NULL; 

    if (pid == 0) { 
     if (execvp(tokens[0].c_str(), arg) == -1) { 
      cout << "Command not known. " << endl; 
     }; 
    } else { 
     wait(); 
    } 
} 

Ce que je veux est que quand j'atteins ce moment-là, la commande sera traitée comme un appel de programme, donc je crée un enfant pour l'exécuter. Cela fonctionne presque parfait, mais je reçois l'invite à nouveau avant la sortie du programme. Exemple:

[email protected]:~/NetBeansProjects/Shell2$ whoami 
[email protected]:~/NetBeansProjects/Shell2$ tronfi 

[email protected]:~/NetBeansProjects/Shell2$ 

L'enfant doit mourir après la execvp, donc il ne faut pas appeler l'invite, et le parent attend jusqu'à ce que la matrice de l'enfant.

Alors ... qu'est-ce que je fais mal?

Merci!

+1

Je ne pense pas que ce soit précisément votre problème, mais considérons ce qui se passe si 'execvp()' échoue. Combien d'instances de votre coquille auriez-vous à ce moment-là? –

+2

Je pense que le titre de cette question va déclencher ma crise de mi-vie ... – David

+0

Ok Greg, je suppose que je devrais avoir à tuer l'enfant du parent. Ai-je tort? –

Répondre

3

Vous n'appelez pas correctement le wait(). Il attend à transmettre un pointeur à int, dans lequel l'état de la sortie de l'enfant sera stocké:

int status; 
wait(&status); 

Vraiment, cependant, vous devez utiliser waitpid() pour vérifier l'enfant spécifique que vous êtes après. Vous devez également faire une boucle autour si waitpid() est interrompue par un signal:

int r; 
do { 
    r = waitpid(pid, &status, 0); 
} while (r < 0 && errno == EINTR); 
+0

Merci beaucoup. Cela fonctionne parfaitement en ce moment. Ce que je ne comprends pas, c'est comment le parent sait quel est le PID de l'enfant, parce que ce «pid» devrait être le pid de l'enfant, n'est-ce pas? Et j'entre en "mode enfant" quand pid == 0 ... J'espère que vous avez compris ma question! –

+0

@Tronfi: Oui, c'est le PID de l'enfant - le parent le sait parce que c'est la valeur retournée par 'fork()', qui est stockée dans la variable 'pid' dans votre exemple. – caf

3

Je ne suis pas sûr que ce soit exactement le problème, mais vous devez vous assurer que l'enfant sort même si execvp() échoue:

if (pid == 0) { 
    if (execvp(tokens[0].c_str(), arg) == -1) { 
     cout << "Command not known. " << endl; 
    }; 
    exit(1); // or some other error code to indicate execvp() fails 
} else { 
    wait(); 
} 

Si vous ne le faites pas, alors si excecvp() échoue alors vous se terminera par deux instances de votre shell, ce qui n'est probablement pas ce que vous voulez.

+0

Merci Greg, vous avez raison. Mais cela ne résout pas mon problème>< –

0

L'enfant doit être mis fin à l'appel à l'aide

exit(0)
(uniquement en cas de succès), car cela aide à clening de la mémoire et le tampon débusque. Ce statut retourné par l'enfant doit être vérifié par le parent et seulement il doit donner l'invite. Faites-moi savoir si vous avez besoin de plus de détails.