2015-05-29 12 views
6

Ho un programma che scarica i dati pcap raccolti usando libpcap su stdout usando la funzione pcap_dump, con stdout come FILE *. C'è un po 'di pulizia necessaria su SIGINT, quindi lo gestisco con sigaction(). Funziona bene quando viene eseguito da una shell.Il segnale SIGINT viene eliminato durante la scrittura su una pipe

Tuttavia, questo programma è destinato a essere chiamato da un altro programma, che non sembra funzionare. Questo programma "chiamante" chiama un pipe(), quindi un fork(), quindi il descrittore del file stdout del figlio viene chiuso e sostituito con la fine della scrittura del pipe. Infine, il suddetto programma pcap viene eseguito nel processo figlio. In questo modo i dati di pcap vengono scritti nel programma chiamante attraverso la pipe. Anche questo funziona bene. Tuttavia, quando invio un SIGINT al processo figlio mentre sta scrivendo sulla pipe (beh, il programma pcap pensa che stia scrivendo su stdout, ma il suo descrittore di file è stato modificato), il segnale sembra essere caduto e la funzione di gestione del segnale non viene mai chiamato.

Perché è quello? Se scrivo i dati di pcap su stderr o su un file, SIGINT non viene mai rilasciato. Solo quando si scrive alla pipe.

Ecco come stiamo allestendo il tubo/forchetta/esecuzione:

int fd[2]; 

//Create pipe 
pipe(fd); 

pid = fork(); //We forked a child 

if(pid == 0){ //We are the child now 

    close(1); //close child's stdout 

    dup(fd[1]); //duplicate child's stdout to the write end of the pipe 

    close(fd[0]); //close unused file descriptors 
    close(fd[1]); 

    //Load the new program 
    execlp("./collectraw", "collectraw", NULL); 

    perror("Exec"); 
    exit(127); //Should never get called but we leave it so the child 
    //doesnt accidently keep executing 
} 
else{ //We are the parent 

    //Set up the file descriptors 
    close(fd[1]); 
} 

poi di uccidere il bambino che usiamo:

kill(pid, SIGINT); 

Nel bambino, la funzione di callback per la nostra pcap_loop() può essere semplice come:

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet){ 
    write(1,"<pretend this is like a thousand zeros>",1000); //write to stdout, which is really a pipe 
} 

E in pratica abbandoneremo sempre SIGINT. A proposito, ci sono molti pacchetti da catturare, quindi è abbastanza sicuro presumere che sia quasi sempre nella funzione di callback.

Ma se ci si sposta dall'attuale

write(1,...); //write to stdout, which is really a pipe 

a

write(2,...); //write to stderr, or writing to a file would work too 

poi tutto diventa ancora rose e fiori.

Perché il nostro SIGINT viene lasciato cadere durante una scrittura su una pipe?

Grazie per l'aiuto.

MODIFICA: il gestore SIGINT del bambino non è mai stato chiamato, ma il motivo non era un problema nel bambino, era un problema nel genitore. Ho usato per uccidere il bambino come:

if(kill(pid, SIGINT) == -1){ 
    perror("Could not kill child"); 
} 
close(pipefd); 
fprintf(stdout, "Successfully killed child\n"); 

E questo era il nostro gestore di SIGCHLD:

void handlesigchild(int sig) { 
    wait(); 

    printf("Cleaned up a child\n"); 
} 

Così, come descritto nella risposta accettata, chiudendo immediatamente il tubo stava causando il nostro bambino per uscire con SIGPIPE prima che il SIGINT venisse gestito. Abbiamo appena spostato il close (pipefd) al gestore SIGCHLD e ora funziona.

+0

Benvenuti in Stack Overflow. Si prega di leggere la pagina [Informazioni] a breve. In generale, dovresti scegliere C o C++ come tag del linguaggio e non entrambi - perché le soluzioni adatte al C++ di solito non sono adatte a C, e spesso succede anche il contrario. –

+0

Cosa mostra l'esecuzione dei processi sotto 'strace'? (Supponendo che Linux sia il tuo sistema operativo) Inoltre, pubblica il tuo codice di gestione dei segnali. –

+0

Potrebbe essere perché il gestore del segnale (supponendo che ne abbiate uno) non chiude il tubo (che è attualmente in uso)? Forse vale la pena provare. – Addison

risposta

2

Non si mostra abbastanza del codice per sapere cosa sta succedendo. Dovresti sempre provare a costruire un SSCCE e postarlo se vuoi che le persone possano commentare il tuo programma.

Migliore ipotesi: il genitore esce dopo aver inviato il segnale, chiudendo l'estremità letta del tubo.Questo fa sì che il client esca immediatamente con un SIGPIPE, prima che abbia la possibilità di gestire SIGINT. Prova a eseguire la pulizia anche su SIGPIPE oppure ignora SIGPIPE.

+0

Fantastico, grazie. Il nostro genitore in realtà non esce, ma stava chiudendo la pipa immediatamente dopo aver inviato SIGINT al bambino, che aveva lo stesso effetto. Aggiornerò il post originale per mostrare cosa stava succedendo. – rexroni