2016-03-02 15 views
5

Wdrażam powłokę.Dlaczego execvp() wykonuje dwukrotnie używając fork()?

Po uruchomieniu polecenia innego niż zmiana katalogu, execvp() działa, dziecko kończy działanie i tworzone jest nowe dziecko. Kiedy zmieniam katalogi, dziecko się nie kończy i powstaje nowe dziecko. Oto przykład z mojego kodu:

for(;;) { 
    printf("bash: "); 
    parse(); 
    ... 
    pid_t pid = fork() 
    if (pid == 0) 
     if (!strcmp(line[0], "cd")) 
      if (!line[1]) (void) chdir(getenv("HOME")); 
      else (void) chdir(line[1]); 
     else execvp(line[0], line); 
    ... 
    if (pid > 0) { 
     while (pid == wait(NULL)); 
     printf("%d terminated.\n", pid); 
    } 
} 

cd ../; ls; działa poprawnie, z wyjątkiem muszę Ctrl+D dwukrotnie, aby zakończyć program.

Mimo to, jeśli wypiszę te same informacje (np. mybash < chdirtest), to działa poprawnie raz, kończy dziecko, ponownie działa, z wyjątkiem oryginału bezpośrednio, a następnie kończy ostatnie dziecko.

+3

Po uruchomieniu 'cd' (niekompletny) w kodzie jako pokazu nie będzie powoływać' execvp'. Zamiast tego zrobi to 'chdir', a następnie będzie kontynuował następną iterację' for', która wywoła 'fork'. Dlatego pierwszy proces potomny nie wychodzi z przypadku 'cd'. – kaylum

+4

Witamy w przepełnieniu stosu. Proszę wkrótce zapoznać się ze stroną [Informacje]. Moje bezpośrednie ujęcie jest takie, że nie ma tu wystarczającej ilości informacji, abyśmy mogli odpowiedzieć - diabeł tkwi w szczegółach, a szczegółów brakuje. Jednak @kaylum stanowi przynajmniej część problemu i prawdopodobnie zidentyfikował główne problemy. (Zaobserwuję, że warunkowe po 'if (pid == 0)' petrify mnie - niewystarczające nawiasy klamrowe i brak obsługi błędów, jeśli 'execvp()' lub 'chdir()' zawodzi.) Proszę przeczytać, jak aby utworzyć MCVE ([MCVE]), a następnie je udostępnić. –

+1

@kaylum Nie chcę, aby 'cd' wywoływał' execvp() '. Chcę, aby zmienił katalog, powrócił do pętli, a następnie uruchom następną linię bez problemów. –

Odpowiedz

5

cd nie należy wywoływać przez proces potomny, sama powłoka powinna zmienić swój bieżący katalog (jest to właściwość wewnętrznego polecenia: modyfikować proces samej powłoki).

A (primitve) powłoka powinna wyglądać tak:

for(;;) { 
    printf("bash: "); 
    parse(); 

    // realize internal commands (here "cd") 
    if (!strcmp(line[0], "cd")) { 
     if (!line[1]) (void) chdir(getenv("HOME")); 
     else (void) chdir(line[1]); 
     continue; // jump back to read another command 
    } 

    // realize external commands 
    pid_t pid = fork() 
    if (pid == 0) { 
     execvp(line[0], line); 
     exit(EXIT_FAILURE); // wrong exec 
    } 

    // synchro on child 
    if (pid > 0) { 
     while (pid == wait(NULL)); 
     printf("%d terminated.\n", pid); 
    } 
} 
Powiązane problemy