|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
[C] Gestione dei segnali
Ciao e auguri a tutti
![]() Volevo sapere se questa è una giusta gestione del segnale SIGINT, perché quando faccio partire il programma e premo CTRL-C mi va in segmentation fault e non riesco a capirne la causa. Questo è il codice del main (ancora provvisorio): Codice:
#include "functions.h" int main(void){ int i; pid_t pid; int status; ssize_t nread; ssize_t nwrite; struct sigaction sigint_handl; char *buf=(char *)malloc(sizeof(char)*COMM_LENGTH); char *name=(char *)malloc(sizeof(char)*COMM_LENGTH); sigint_handl.sa_handler=sigint_handler; if (sigemptyset(&sigint_handl.sa_mask)!=0){ perror("sigemptyset failed"); exit(SIGH_ERR); } sigint_handl.sa_flags=0; if(sigaction(SIGINT,&sigint_handl,NULL)){ perror("sigaction failed"); exit(SIGH_ERR); } setjmp(chdir_return); setjmp(sigint_return); while(1){ sprintf(buf,"%s $ ", get_current_dir_name()); if ((nwrite=write(STDOUT_FILENO,buf,COMM_LENGTH)) != COMM_LENGTH){ perror("write error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(ERR_GEN); } if( (nread = read(STDIN_FILENO,buf,COMM_LENGTH)) <= 0){ perror("read error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(ERR_GEN); } strcpy(name,buf); parse(name); if(command_lines(name) == OK){ if((pid=fork()) < 0){ perror("fork error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(FORK_ERR); } if(pid > 0){ if (waitpid(pid,&status,0) == -1){ perror("waitpid error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } } } } } return 0; } Codice:
#include "functions.h" void sigint_handler(int signo){ if(signo != SIGINT){ printf("Something wrong, handler for SIGINT"); exit(SIGH_ERR); } else longjmp(sigint_return,0); } ![]() |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Nov 2005
Città: TO
Messaggi: 5206
|
Prendila come una mia opinione personale ... credo che non sia una buona idea mettere un longjmp() nell'handler.
__________________
Andrea, SCJP 5 (91%) - SCWCD 5 (94%) |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Perché no? E come faccio a tornare qua "sprintf(buf,"%s $ ", get_current_dir_name());" (che sarebbe il prompt della shell)?
|
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
In quel caso la read ritorna -1 con errno==EINTR.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Ah quindi dovrei controllare se la read torna EINTR (e se lo torna lanciare il signal handler) ? Un'altra possibilità. Mettiamo il caso che io voglia eseguire ls e mi trovo in una directory con tantissimi files. L'esecuzione di ls non è istantanea ma ci mette un po' per listare tutto il contenuto. Mettiamo il caso che durante l'esecuzione di ls premo CTRL-C. Visto che ls lo dovrei eseguire tramite una exec (a meno di altri suggerimenti da parte vostra
![]() Grazie. |
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Non ti seguo... il segnale viene inviato al tuo processo, non a ls
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Cerco di spiegarmi meglio. Io per eseguire un comando con questa shell (perché in realtà il programma che sto realizzando è una shell) creo un figlio tramite fork() e faccio eseguire al figlio un comando tramite exec(). Il figlio eredita dal padre il signal handler impostato da me, ma nel momento in cui eseguo la exec di un comando, il signal handler ritorna quello di default. Io vorrei fare in modo (se possibile) di eseguire il mio signal handler dopo la exec. Per questo ti avevo fatto l'esempio di ls all'interno di una directory con migliaia di files, perché ad esempio mi stufo di vedere tutto il contenuto e ad un certo punto premo CTRL-C e ritorno al prompt. Spero di essere stato chiaro.
|
![]() |
![]() |
![]() |
#8 | |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Innanzitutto, non puoi passare un _tuo_ signal handler tramite exec, in quanto il tuo spazio di indirizzi (compreso il codice dell'handler!) viene completamente riscritto dalla exec.
Puoi intercettare il segnale dal processo principale e uccidere il child con un segnale, se è ciò che intendi fare. Quote:
Il parent continua ad utilizzare gli handler che ha definito.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Certo è proprio questo il problema, cioè che il child eseguendo la exec non ha più il signal handler del padre. E se faccio un controllo del tipo WIFSIGNALED(status) e WTERMSIG(status) al ritorno dalla wait? Potrei vedere se il figlio è stato terminato con un segnale e con quale segnale no? Però l'unica cosa è che quando so il segnale con il quale è stato terminato, se il segnale ad esempio è SIGINT, quale signal handler viene eseguito?
Grazie. |
![]() |
![]() |
![]() |
#10 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Continuo a non capire...
Come può il child beccarsi un segnale in seguito a ctrl+c dalla console? E' impossibile, a meno che non hai per errore invocato la exec nel ramo del parent della fork e non nel ramo del child...
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Allora facciamo così, cerco di fare un esempio. Il figlio tramite la funzione exec() esegue ls -l e riceve questo output:
file1 file2 file3 file4 file5 ecc. Mettiamo il caso che i file all'interno della directory che sto listando sono tantissimi (sono 10000 file). Io non voglio aspettare che finisca di elencarli tutti (magari ci mette un po) quindi ad un certo punto, prima che finisce di elencare tutto il contenuto, premo CTRL-C. Una volta premuto dovrà eseguire un signal handler (che per forza di cose sarà quello di default dato che ho eseguito la exec()). Io però devo fare in modo di tornare a questa istruzione "sprintf(buf,"%s $ ", get_current_dir_name()); ed è per questo che mi serve il mio signal handler. |
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Se posti un pò di codice forse possiamo intenderci meglio...
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
main.c
Codice:
#include "functions.h" int main(void){ extern int errno; int i; pid_t pid; int status; ssize_t nread; ssize_t nwrite; struct sigaction sigint_handl; char *buf=(char *)malloc(sizeof(char)*COMM_LENGTH); char *name=(char *)malloc(sizeof(char)*COMM_LENGTH); sigint_handl.sa_handler=sigint_handler; if (sigemptyset(&sigint_handl.sa_mask)!=0){ perror("sigemptyset failed"); exit(SIGH_ERR); } sigint_handl.sa_flags=0; if(sigaction(SIGINT,&sigint_handl,NULL)){ perror("sigaction failed"); exit(SIGH_ERR); } while(1){ sprintf(buf,"%s $ ", get_current_dir_name()); if ((nwrite=write(STDOUT_FILENO,buf,COMM_LENGTH)) != COMM_LENGTH){ perror("write error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(ERR_GEN); } if( (nread = read(STDIN_FILENO,buf,COMM_LENGTH)) <= 0){ if(errno == EINTR) continue; else{ if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(ERR_GEN); } } strcpy(name,buf); parse(name); if(command_lines(name) == OK){ if((pid=fork()) < 0){ perror("fork error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } exit(FORK_ERR); } if(pid == 0{ if(pid > 0){ if (waitpid(pid,&status,0) == -1){ perror("waitpid error"); if (buf!=NULL){ free(buf); buf=NULL; } if (name!=NULL){ free(name); name=NULL; } continue; } if(WIFSIGNALED(status)) signoris=WTERMSIG(status); } } } return 0; } Codice:
/* Librerie */ #include <string.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <ctype.h> #include <unistd.h> #include <signal.h> #include <glob.h> #include <errno.h> #include <setjmp.h> #include <sys/wait.h> #include <sys/types.h> /* Definizione di costanti */ #define MAX_ARGS 256 /* Numero massimo di argomenti sulla linea di comando */ #define COMM_LENGTH 256 /* Lunghezza massima di un comando */ /* Costanti per errori */ #define OK 0 /* OK */ #define F_NOTEXEC 1 /* File non eseguibile */ #define F_NOEXIST 2 /* Accesso negato o file inesistente */ #define ERR_GEN 3 /* Errore generico */ #define READ_ERR 4 /* Read error */ #define WRITE_ERR 5 /* Write error */ #define FORK_ERR 6 /* Fork error */ #define SIGH_ERR 7 /* Errore del signal handler */ /* Variabili globali */ char *args[MAX_ARGS]; /* Argomenti sulla linea di comando */ glob_t pglob; /* Variabile per il trattamento del filename expansion */ /* Definizione di funzioni */ int command_lines(char *); /* Cerca la locazione del comando e restituisce il pathname completo */ void parse(char *); /* Analizza il comando passato suddividendo gli argomenti */ void sigint_handler(int); /* Signal handler per CTRL-C */ void sigquit_handler(int); /* Signal handler per CTRL-\ */ Codice:
#include "functions.h" void sigint_handler(int signo){ if(signo != SIGINT){ printf("Something wrong, handler for SIGINT"); exit(SIGH_ERR); } else return; } |
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Codice:
if(pid == 0{ if(pid > 0){ ![]()
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#15 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Il codice non è completo (mica ho finito tutto)
![]() E quello è proprio il famoso ramo child che devo implementare (sempre che quella soluzione vada bene per quello che devo fare io). |
![]() |
![]() |
![]() |
#16 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Comunque, come per le altre funzioni, in caso di segnale intercettato la waitpid ritorna -1 con errno==EINTR. Puoi, in questo caso, inviare manualmente un SIGTERM al child; ad es. qualcosa tipo:
Codice:
if(pid > 0){ if (waitpid(pid, &status, 0) == -1) { if (errno!=EINTR) perror("waitpid") else { kill(pid, SIGTERM); /* necessario per evitare uno zombie */ do { if (waitpid(pid, &status, 0)!=-1) break; } while(errno==EINTR); } continue; } else { <ramo child> }
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Ok però in questo caso sono io che mando un segnale esplicitamente. Io invece devo intercettare un segnale proveniente dal figlio (nel mio caso CTRL-C). Perché, ripeto, durante la exec() nel ramo child, nell'ipotesi in cui io prema CTRL-C deve partire il signal handler creato da me. Nel tuo codice:
Codice:
if (waitpid(pid, &status, 0) == -1) { if (errno!=EINTR) perror("waitpid") else { kill(pid, SIGTERM); |
![]() |
![]() |
![]() |
#18 | |||
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
Quote:
Quote:
Esercitati con questo programmino, modificandolo a tuo piacimento, per vedere come funziona il tutto: Codice:
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> static void sigint_handler(int signo) { fprintf(stderr, "Caught signal %d\n", signo); } int main() { struct sigaction sigint_handl; pid_t pid; sigint_handl.sa_handler = sigint_handler; sigemptyset(&sigint_handl.sa_mask); sigint_handl.sa_flags = 0; sigaction(SIGINT, &sigint_handl, NULL); pid = fork(); if (pid==0) { char *args[] = { "sleep", "10", NULL }; /* Senza setsid, il ctrl+c giunge anche al child. Potrebbe essere desiderabile, oppure no. Nota che un kill -INT <pid del parent> non giunge anche al child! */ setsid(); execv("/bin/sleep", args); perror("execv"); } else { while(1) { int status; int ret = waitpid(pid, &status, 0); if (ret<0) { if (errno!=EINTR) perror("waitpid"); else { fprintf(stderr, "Killing child...\n"); /* Nel caso che: * 1) Il segnale non sia stato inviato anche al child * (ad es. un SIGINT mandato direttamente al parent) * 2) Il child gestisce e non termina su SIGINT * allora accertiamoci di terminare il child manualmente. */ kill(pid, SIGTERM); } } else { fprintf(stderr, "Child done. status=%d\n", status); break; } } } return 0; }
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|||
![]() |
![]() |
![]() |
#19 |
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
...il tutto, beninteso, se ho capito cosa ti serve: su ctrl+c, accertarti che il child ("ls" nel tuo caso) sia annullato.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
![]() |
![]() |
![]() |
#20 |
Senior Member
Iscritto dal: Jan 2001
Città: Villanova di Guidonia (RM)
Messaggi: 1079
|
Ok, grazie ci proverò.
![]() |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 05:24.