Problema nella gestione di segnali con sigaction

di il
14 risposte

Problema nella gestione di segnali con sigaction

Salve a tutti!
Ho un problema con un programma scritto in c. Esso riproduce, con alcuni difetti, una shell. Il processo padre dopo aver preso in input il comando da eseguire genera , con una fork(), un secondo processo che a sua volta esegue il comando dato.
Ora con una sigaction io vorrei far si che il segnale SIGINT(inviato da tastiera con Ctrl+c) venga ignorato dal processo padre ma non dal processo figlio, che dovrà terminare l'esecuzione del comando.
Nel codice postato sono riuscito in questo settando sa_handler nel padre a SIG_IGN e nel figlio risettandolo a SIG_DFL.
So però che oltre a questi 2 valori sa_handler può assumere anche il puntatore ad una funzione del tipo void funzione(int sig).
Ricapitolando se nel figlio setto l'handler a SIG_DFL va, ma se lo setto a funzione tiene lo stesso comportamento che ha settandolo a SIG_DFL.

struct sigaction sa;  //dichiarata come variabile globale

void f_intr(int sig){                     //funzione che vorrei usare al posto di SIG_DFL
	printf("\nTerminate\t%d\t%d\n", getpid(), sig);
}

void runcommand(char **cline,int where)	/* esegue un comando */
{
  pid_t pid, ret;
  int exitstat;
  
  sa.sa_handler = SIG_IGN;      //il processo padre setta handler a SIG_ING 
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGINT, &sa, NULL);  //il comando da intercettare è SIGINT
  
  pid = fork();                         //genera un figlio
  if (pid == (pid_t) -1) {
     perror("smallsh: fork fallita");
     return;
  }

  if (pid == (pid_t) 0) { 	/* processo figlio */
    
    sa.sa_handler = f_intr;  
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL); 

 /*nonostante sa.sa_handler sia = f_intr il comportamento rimane  quello di SIG_DFL */

    execvp(*cline,cline);
    perror(*cline);
    exit(1);
  }

  /* processo padre: avendo messo exec e exit non serve "else" */
 
  if (ret == -1) perror("wait");
}


Qualche idea?

14 Risposte

  • Re: Problema nella gestione di segnali con sigaction

    Proprio nessuno?
  • Re: Problema nella gestione di segnali con sigaction

    mrdaino ha scritto:


    Proprio nessuno?
    Ciao
    Ora con una sigaction io vorrei far si che il segnale SIGINT(inviato da tastiera con Ctrl+c) venga ignorato dal processo padre ma non dal processo figlio, che dovrà terminare l'esecuzione del comando.
    Nel codice postato sono riuscito in questo settando sa_handler nel padre a SIG_IGN e nel figlio risettandolo a SIG_DFL.
    Giusto.
    So però che oltre a questi 2 valori sa_handler può assumere anche il puntatore ad una funzione del tipo void funzione(int sig).
    Esatto
    Ricapitolando se nel figlio setto l'handler a SIG_DFL va, ma se lo setto a funzione tiene lo stesso comportamento che ha settandolo a SIG_DFL.
    sa_handler può essere: SIG_DFL (DeFault), SIG_IGN (IGNore) oppure la tua funzione handler.

    Non avendo trovato niente dalla rete di già pronto, te ne ho scritto uno per il tuo caso specifico:
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>
    
    void sigint (int sig)
    {
    	// Not good idea to printf... :)
    	printf ("\ntrapped!\n");
    }
    int main (int argc, char **argv)
    {
    	pid_t pid, wpid;
    	struct sigaction sa;
    
    	/* assign sigint as our handler */
    	sa.sa_handler = SIG_IGN;
    
    	/* don't want to block any other signals */
    	sigemptyset(&sa.sa_mask);
    	sigaction(SIGINT, &sa, NULL);
    
    	if ((pid=fork())==-1)
    	{
    		perror ("fork");
    		exit(EXIT_FAILURE);
    	}
    	else if (pid)
    	{
    		// parent
    		int status;
    		do
    		{
    			if ((wpid = waitpid(pid, &status, WUNTRACED | WCONTINUED ))== -1)
    			{
    				perror ("waitpid");
    				exit(EXIT_FAILURE);
    			}
    			if (WIFSIGNALED(status))
    				printf ("killed by signal %d\n",WTERMSIG(status));
    			else if (WIFEXITED(status))
    				printf ("exit with status=%d\n",WEXITSTATUS(status));
    			else if (WIFSTOPPED(status))
    				printf ("stopped by signal %d\n",WSTOPSIG(status));
    			else if (WIFCONTINUED(status))
    				printf ("continue\n");
    		}while (!WIFEXITED(status) && !WIFSIGNALED(status));
    
    		exit(EXIT_SUCCESS);
    
    	}
    	else
    	{
    		// child
    
    		/* assign sigint as our handler */
    		sa.sa_handler = sigint;
    
    		/* don't want to block any other signals */
    		sigemptyset(&sa.sa_mask);
    		sigaction(SIGINT, &sa, NULL);
    		//
    		printf ("I'm Child w pid %d\n", getpid());
    		//
    		if (argc == 1)
    		{
    			// wait for signal
    			pause();
    
    			/* assign sigint as default  */
    			sa.sa_handler = SIG_DFL;
    
    			/* don't want to block any other signals */
    			sigemptyset(&sa.sa_mask);
    			sigaction(SIGINT, &sa, NULL);
    		//
    
    
    			raise(SIGINT);
    
    		}
    
    		_exit(atoi(argv[1]));
    	}
    
    	return 0;
    }
    
    
    test:
    
    max@studio:~> # TEST 1
    max@studio:~> ./a.out 123
    I'm Child w pid 17633
    exit with status=123
    max@studio:~>
    max@studio:~> # TEST 2
    max@studio:~> ./a.out 
    I'm Child w pid 17709
    ^C
    trapped!
    killed by signal 2
    max@studio:~>
    max@studio:~> # TEST 3
    max@studio:~> ./a.out &
    [1] 17715
    I'm Child w pid 17717
    max@studio:~> kill -SIGSTOP 17717
    stopped by signal 19
    max@studio:~> kill -SIGCONT 17717
    continue
    max@studio:~> kill -SIGINT 17717
    
    trapped!
    killed by signal 2
    [1]+  Done                    ./a.out
    max@studio:~> 
    
    
    Nota come al ritorno dell' handler eseguo una chiamata raise [equivalente ad una kill (getpid(), SIGINT)] in modo da rievocare lo stesso segnale per catturarlo correttamente dal parent attraverso la wait.

    Credo cumunque che questo sia l'unico esempio valido reperibile in tutta la rete internet. Almeno io non ne ho visti!
  • Re: Problema nella gestione di segnali con sigaction

    Si ok però non capisco cosa ci sia di sbagliato nella mia versione! Soprattutto perchè se cambio l'handler ad una funzione definita da me continua a tenere il comportamento di SIG_DFL?
  • Re: Problema nella gestione di segnali con sigaction

    Gli exec* resettano l'handler signal . Per cui, è inutile mettere la funzione nel child, ma riprendere il segnale dal parent.
    % man 3p execvp
    <---cut--->
    Signals set to the default action (SIG_DFL) in the calling process image shall be set to the default action in the new process image. Except for SIGCHLD, signals set to be ignored (SIG_IGN) by the calling process image shall be set to be ignored by the new process image. Signals set to be caught by the calling process image shall be set to the default action in the new process image (see <signal.h>). If the SIGCHLD signal is set to be ignored by the calling process image, it is unspecified whether the SIGCHLD signal is set to be ignored or to the default action in the new process image. After a successful call to any of the exec functions, alternate signal stacks are not preserved and the SA_ONSTACK flag shall be cleared for all signals.
    <---cut--->
  • Re: Problema nella gestione di segnali con sigaction

    Capisco grazie!!
    Ha senso però non riesco proprio a raggiungere il mio abbiettivo.
    Mettendo la sigaction nel padre sembra che il sengale venga venga diretto verso di esso e infatti mi dice il pid del processo padre dalla funzione f_intr che avevo messo nel primo post. Infatti mi scrive anche questo:
    wait: interrupted system call
    Inquanto dopo la sigaction eseguo una wait per far aspettare al processo padre la terminazione del figlio(o almeno credo sia per questo).
    Perciò come posso intercettare il segnale da mendare durante la exec* nel padre in modo che esso sia diretto al figlio?
  • Re: Problema nella gestione di segnali con sigaction

    Mettendo la sigaction nel padre sembra che il sengale venga venga diretto verso di esso e infatti mi dice il pid del processo padre dalla funzione f_intr che avevo messo nel primo post.
    Nell'esempio che ti ho allegato, lasciando tutto com'è ed aggiungendo la exec non viene richiamata la funzione handler dal child (poichè è resettata), MA viene intercettato il segnale dal parent.
    wait: interrupted system call
    Questo avviene se all'uscita non esegui la raise (o la kill) del child.
    Inquanto dopo la sigaction eseguo una wait per far aspettare al processo padre la terminazione del figlio(o almeno credo sia per questo).
    Questo è giusto, serve essenzialmente ad evitare la formazione dei processi 'zombi'... questo almeno è quello che ricordo
  • Re: Problema nella gestione di segnali con sigaction

    Perciò come posso intercettare il segnale da mendare durante la exec* nel padre in modo che esso sia diretto al figlio?
    Questo non mi e' chiaro. Il figlio invia il segnale al padre, ma se vuoi inviarlo al figlio forse hai bisogno di un nipote... bho...

    Prova a chiederlo a Style, mi sembra di ricordare che aveva scritto una shell
  • Re: Problema nella gestione di segnali con sigaction

    Si quindi in teoria dovrei cercare di risettare il signal handler all'interno del processo figlio appena viene avviata la exec*. Però pensandoci e ripensandoci non mi è venuto proprio in mente un modo per farlo.
    Ho cercato in diverse dispense di diverse università ma l'unica cosa che trovo in merito è:
    -exec continua a ignorare i segnali ingnorati prima di essa;
    -exec resetta il signal handler a default in qualsiasi altro caso(come da descrizione del man postato da te).
    Le spiegazioni si fermano sempre li ma non risolvono mai il mio problema!
    Cioè come posso accedere al signal handler della exec? Vuol dire che divrei risettarlo a mio piacimento durante la sua esecuzione oppure dovrei passare l'handler come argomento ad exec*?
    Oppure esiste una qualche chiamata di sistema che faccia rimanere invarato handler del processo anche dopo la exec?
    Una delle possibilità che ho elencato potrebbe essere quella giusta?
    Non so più dove sbattere la testa...
  • Re: Problema nella gestione di segnali con sigaction

    Ah ok scusa non avevo letto l'ultimo messaggio.
    Scusa come faccio a chiedere alla persona in questione?
  • Re: Problema nella gestione di segnali con sigaction

    La trovi su questo forum... guarda quache post sotto.
    edit:http://www.iprogrammatori.it/forum-programmazione/member10982.html
  • Re: Problema nella gestione di segnali con sigaction

    Ok grazie mille. Alla fine sono riuscito a fare quello che dovevo senza stare a vedere l'handler dopo la exec ma semplicemente usando una macro sul secondo parametro passato alla waitpid!
    Grazie comunque per l'impegno sei stato veramente gentile!
    grazie ancora ciao!
  • Re: Problema nella gestione di segnali con sigaction

    Prego,
    sono contento che hai risolto, ma non mi è chiara la macro sul 2^ parametro (forse ti confondi col terzo) della waitpid... e quale definizione hai aggiunto?
  • Re: Problema nella gestione di segnali con sigaction

    //pid_t waitpid(pid_t pid, int *status, int options);
    il parametro in questione è int *status che avevi usato anche tu nel codice che mi avevi postato.
    Dovevo usare la macro WIFEXITED() su status per vedere se il figlio ritornava dopo aver concluse il processo o se era stato bloccato da un segnale.
    Praticamente a me non bastava settare l'handler nel figlio a SIG_DFL perche volevo che ad un SIGINT oltre a terminare mi stampasse a video "Terminato" e il pid del child. Solo che ero talmente lanciato sulla mia idea che non mi veniva in mente che c'era un'altro modo molto più facile, senza cercare di risettare l'handler dopo la exec*.
    Scusa ma sto ancora imparando e devo entrare bene nell'ottica per non farmi fermare da problemucci di questo genere..
    Grazie ancora!
  • Re: Problema nella gestione di segnali con sigaction

    Ahhh, si, si le varie macro che estraggono dallo status. Senza dubbio sempre sono indispensabili.
    Ed io che pensavo ti riferissi alle definzioni del terzo parametro
    Grazie, ciao
Devi accedere o registrarti per scrivere nel forum
14 risposte