2013-10-04 3 views
5

Un'assegnazione nella mia classe Sistemi operativi mi richiede di costruire un albero di processo binario chiamando recursivamente exec sullo stesso programma. L'obiettivo è dividere alcune attività arbitrarie in processi separati. Il genitore dovrebbe comunicare con i bambini e i figli con il genitore solo tramite pipe senza nome. L'idea è che il genitore manda ogni bambino a metà del lavoro e questo continua in modo ricorsivo fino a quando viene raggiunto un caso base in cui la lunghezza della stringa passata a ogni bambino è < = 2. Il bambino quindi elabora questi dati e li restituisce i risultati al genitore tramite pipe.Tubo, forcella ed Exec - Comunicazione bidirezionale tra processo padre e figlio

Per una migliore comprensione di come la comunicazione bidirezionale funziona con le tubazioni in c ho creato il seguente programma semplice prima di passare all'assegnazione effettiva. Tuttavia, il genitore non legge mai i dati dal processo figlio. Mi aspetto l'uscita ...

nel genitore | messaggio ricevuto: prova

Invece, quando stampo ricevo ...

in superiore | messaggio ricevuto:

Sembra che il buff sia vuoto e non stia leggendo dal processo figlio. Qualcuno può spiegare quello che sto facendo male e/o il modo standard di

  1. scrivendo a exec'd bambino da genitori
  2. lettura da genitore a figlio exec'd
  3. iscritto a padre da bambino exec'd
  4. lettura da bambino exec'd nel genitore

sono tenuto a utilizzare exec(), pipe(), fork(). Grazie.

/** 
* ********************************* 
* two_way_pipes.c 
* ********************************* 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <math.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <unistd.h> 

#define PARENT_READ read_pipe[0] 
#define PARENT_WRITE write_pipe[1] 
#define CHILD_WRITE read_pipe[1] 
#define CHILD_READ write_pipe[0] 

#define DEBUGGING 1 

int main(int argc, char **argv) { 
    char buff[5]; 

    // in the child process that was exec'd on the orginal call to two_way_pipes 
    if(argc == 2) { 
     read(STDIN_FILENO, buff, 4); // this should read "test" from stdin 
     buff[4] = '\0'; 
     fprintf(stdout, "%s\n", buff); // this should right "test" to stdout and be read by the parent process 
    // int the root process, the original call to two_way_pipes with no args 
    } else { 
     int pid; 
     int read_pipe[2]; 
     int write_pipe[2]; 

     pipe(read_pipe); 
     pipe(write_pipe); 

     pid = fork(); 

     // parent process 
     if(pid > 0) { 
      close(CHILD_READ); 
      close(CHILD_WRITE); 

      write(PARENT_WRITE, "test", 4); // attempting to write this to the child 

      struct timeval tv; 
      fd_set readfds; 
      tv.tv_sec = 10; 
      tv.tv_usec = 0; 
      FD_ZERO(&readfds); 
      FD_SET(PARENT_READ, &readfds); 
      select(PARENT_READ + 1, &readfds, NULL, NULL, &tv); 

      if(FD_ISSET(PARENT_READ, &readfds)) { 
       read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout 
       buff[4] = '\0'; 
       close(PARENT_READ); 
       close(PARENT_WRITE); 
       fprintf(stderr, "in parent | message received: %s\n", buff); // "test" is not in buff 
      } 

     // child process 
     } else if(pid == 0) { 
      close(PARENT_READ); 
      close(PARENT_WRITE); 

      dup2(CHILD_READ, STDIN_FILENO); 
      dup2(CHILD_WRITE, STDOUT_FILENO); 
      close(CHILD_READ); 
      close(CHILD_WRITE); 

      char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL}; 
      execvp("two_way_pipes", argv2); 
      _exit(0); 
     // error forking child process 
     } else { 
      fprintf(stderr, "error forking the child\n"); 
     } 
    } 
} 

Aggiornamento

in base alla risposta di Jonathon ho modificato la matrice arg2 essere passato nel execvp a ...

char *argv2[] = {"two_way_pipes", "1", NULL}; 
execvp("two_way_pipes", argv2); 

Questo non risolvere il problema. Il genitore non era ancora in grado di leggere "test" dal client. Tuttavia, in risposta alla risposta di Jonathon e al commento di William, ho iniziato a modificare la mia chiamata exec e, per qualche motivo, cambiarla con la linea show sotto funzionante.

execl("two_way_pipes", "two_way_pipes", "1", NULL); 

Accetterò volentieri tutte le risposte che spiegano il motivo per cui la chiamata execvp non avrebbe funzionato, ma la chiamata execl ha fatto.

+0

Il tuo exevp ha avuto successo?Prova a sostituire _exit (0) con 'perror (" execvp ")' –

+0

Grazie William. Ho sostituito la chiamata _exit (0) con perror ("execvp"). Quel messaggio di errore non è mai stato stampato, ma ho iniziato a modificare la mia chiamata exec e per qualche motivo cambiando la chiamata a execl ("two_way_pipes", "two_way_pipes", "1", NULL); risolto il problema –

+0

Questo codice manca di conseguenza per verificare l'esito delle chiamate di sistema. In caso contrario, il testing e il debug sono difficili e il programma è debole. Per favore vedi la mia risposta. – alk

risposta

6

Oltre the issue mentioned da Jonathon Reinhart, molto probabilmente la chiamata a execv() fallisce.

per testare questo modificare queste linee

execvp("two_way_pipes", argv2); 
_exit(0); 

di essere

... 
#include <errno.h> 
... 


execvp("two_way_pipes", argv2); /* On sucess exec*() functions never return. */ 
perror("execvp() failed); /* Getting here means execvp() failed. */ 
_exit(errno); 

aspettarsi di ricevere

execvp() failed: No such file or directory 

Per risolvere questo cambiamento

execvp("two_way_pipes", argv2); 

di essere

execvp("./two_way_pipes", argv2); 

Anche se il bambino non era exec*() ed allora questa linea

read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout 

fallisce e, a sua volta buff non è inizializzata e quindi questa linea

fprintf(stderr, "in parent | message received: %s\n", buff); 

provoca un comportamento indefinito.

Per correggere questo inizializzare almeno correttamente buff modificando

char buff[5]; 

essere

char buff[5] = ""; 
+0

Non è davvero un grosso problema, come succede sempre - Ma è impressionante quanto spesso le persone sbagliano a digitare il nome di qualcuno - anche quando si trova a un centinaio di * pixel * di distanza sulla stessa pagina. –

+3

Mi dispiace per quello. Ho il sospetto che se fossi chiamato Anthany, la gente lo farebbe anche a me :-) –

+0

@ JonathonReinhart: Sry Jonathon, corretto. – alk

2

La prima voce nel tuo argv2 dovrebbe essere il nome dell'eseguibile (proprio come il tuo argv[0] in arrivo.

char *argv2[] = {"two_way_pipes", "some random arg...", NULL}; 
execvp("two_way_pipes", argv2); 

Dal execvp man page:

Il execv(), execvp() e execvpe() funzioni fornire un array di puntatori a terminazione null stringhe che rappresentano la lista degli argomenti a disposizione del nuovo programma. Il primo argomento, per convenzione, dovrebbe puntare al nome file associato al file in esecuzione. L'array di puntatori deve terminare con con un puntatore NULL.

+0

Grazie Jonathan. Ho modificato argv2 in base alla risposta, ma il genitore non sta ancora recuperando i dati dal figlio di exec'd. –

1

Come Jonathon Reinhart dice che si dovrebbe cambiare queste righe:

char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL}; 
execvp("two_way_pipes", argv2); 

a:

char *argv2[] = {argv[0], "some random arg...", NULL}; 
execvp(argv[0], argv2); 

allora funziona come previsto:

echo test | ./two_way_pipes 
in parent | message received: test 

Nel programma si ha scritto "two_way_pipes", ma non si trova nel percorso così si ha realmente bisogno l'extra ./ modo argv [0] allora è (" ./two_way_pipes ").