2012-12-26 10 views
11

Sto scrivendo un mini-shell (no, non per scuola: P; per il mio divertimento personale) e la maggior parte delle funzionalità di base è stata eseguita ma sono bloccato quando provo a gestire SIGTSTP.blocco waitpid quando non dovrebbe

Presumibilmente, quando un utente preme Ctrl+Z, SIGTSTP deve essere inviato al processo Foreground della shell se esiste e Shell dovrebbe continuare normalmente.

Dopo la creazione di ogni processo (se si tratta di un processo in primo piano), il seguente codice attende:

if(waitpid(pid, &processReturnStatus, WUNTRACED)>0){//wait stopped too 
    if(WIFEXITED(processReturnStatus) || WIFSIGNALED(processReturnStatus)) 
     removeFromJobList(pid); 
} 

E io sto gestendo il segnale come segue:

void sigtstpHandler(int signum) 
{ 
    signum++;//Just to remove gcc's warning 
    pid_t pid = findForegroundProcessID(); 
    if(pid > -1){ 
     kill(-pid, SIGTSTP);//Sending to the whole group 
    } 
} 

quello che succede è che quando preme Ctrl+Z, il processo figlio viene effettivamente sospeso (utilizzando ps -all per visualizzare lo stato dei processi) ma la mia shell si blocca a waitpid it neve r restituisce anche se ho passato il flag WUNTRACED che, per quanto ho capito, dovrebbe restituire waitpid anche quando il processo viene interrotto.
Quindi cosa avrei potuto fare di sbagliato? o ho capito male il comportamento di waitpid in modo errato?

Note:
-findForegroundProcessID() restituisce il pid corretto; L'ho ricontrollato.
-I sto cambiando il gruppo di ogni processo, quando subito dopo ho fork
-la gestione Ctrl+C sta lavorando bene
-Se io uso un altro terminale per inviare SIGCONT dopo i miei si blocca di shell, il processo figlio riprende il suo lavoro e il guscio si miete infine.
-Sto catturando SIGTSTP che può essere catturato per quanto ho letto (e testato). -Ho provato ad usare waitid invece di waitpid nel caso in cui il problema persistesse. EDIT:

void sigchldHandler(int signum) 
{ 
    signum++;//Just to remove the warning 
    pid_t pid; 
    while((pid = waitpid(-1, &processReturnStatus, 0)) > 0){  
     removeFromJobList(pid); 
    } 
    if(errno != ECHILD) 
     unixError("kill error"); 
} 

mio gestore di SIGCHLD.

+0

IIUC, stai cercando di catturare SIGSTP. Non puoi prendere SIGSTP o SIGKILL. – wildplasser

+1

Non sono sicuro che SIGSTOP sia lo stesso di SIGSTP ma secondo un libro, SIGSTOP non può essere catturato ma SIGTSTP può, in effetti sono sicuro che l'ho preso perché ho stampato qualcosa come test – Fingolfin

+0

Oops, my bad. Se ho letto correttamente, SIGTSTP è stato creato intenzionalmente per consentirne il rilevamento, per consentire al gruppo di processi/al gruppo terminale di propagarlo tra i suoi membri. Se il libro a cui fai riferimento è APERTO, probabilmente sarai in grado di farlo correttamente ... – wildplasser

risposta

1

SIGCHLD viene consegnato per i bambini fermati. Il waitpid() chiama nel gestore di segnale - che non specifica WUNTRACED - blocchi per sempre.

Probabilmente non si dovrebbe avere l'elaborazione removeFromJobList() in due posti diversi. Se dovessi indovinare, sembra che tocchi strutture di dati globali e non appartenga a un gestore di segnali.

+0

Avrei dovuto leggere di più su SIGCHLD, anzi è stato colto da ciò che ha causato l'attesa per bloccare, non era l'attesa nel processoCreazione piuttosto l'attesa in il gestore del segnale. Grazie mille =) – Fingolfin

1

Waitpid non viene restituito perché non si sta impostando un gestore sigchld (che ti ho inviato in precedenza). Hai processori per bambini che non vengono raccolti. Inoltre, waitpid deve essere in un ciclo while, non un if (anche te inviato).

L'unico segnale che dovresti prendere è SIGCHLD. La ragione è che se i tuoi processi sono biforcati correttamente, il kernel invierà quel segnale al processo in primo piano e lo interromperà o lo fermerà o farà qualsiasi cosa il segnale sia correttamente.

Quando i gruppi di processi non sono impostati correttamente, i segnali verranno inviati al processo sbagliato. Un modo per testare è eseguire un processo in primo piano e premere Ctrl-Z.Se esiste un'intera shell, il segnale Ctrl-Z viene inviato all'intera shell. Ciò significa che non hai impostato il nuovo processo in un nuovo gruppo di processi e gli hai dato un terminale.

Ora ecco cosa devi fare se il segnale Ctrl-Z sta arrestando l'intera shell. Una volta avviato un processo, nel processo figlio: - Imposta il processo nel proprio gruppo utilizzando setpgid. - Dagli un terminale sano bloccando SIGTTOU e assegnandogli il terminale usando tcsetpgrp.

Nel genitore: - Impostare anche il processo figlio utilizzando setpgid. Questo perché non hai idea se il bambino o il genitore verranno eseguiti per primi, quindi questo evita una condizione di competizione. Non fa male impostarlo due volte.

+0

Grazie per la risposta, sto impostando un sigchld hanlder (da qui il motivo per cui la gestione di Ctrl + C funziona bene). Questo suona un po 'strano, stai dicendo che l'unico segnale che dovrei gestire è SIGCHLD? ma non ricevo SIGCHLD quando un bambino riceve un segnale SIGTSTP ma penso che potrei aver capito cosa intendi. L'idea è di dare un nuovo processo al proprio terminale e renderlo in primo piano così quando Ctrl + Z va, colpisce solo il processo in primo piano e non la mia shell senza gestire Ctrl + Z del tutto? – Fingolfin

+0

Sto impostando il mio gestore di segnale, come ho già detto sopra, e ho confermato che ora il processo figlio sta ricevendo il segnale e si sta fermando, ma l'attesa non sta ancora ritornando. – Fingolfin