Signalverwaltung
Signale in Multithread-Prozessen sind eine Erweiterung von Signalen in traditionellen Einzelthread-Programmen.
- Prozessbezogene Signalhandler
- Threadspezifische Signalmasken
- Einzelne Zustellung jedes Signals
Signalhandler und Signalmasken
Signalhandler werden auf Prozessebene verwaltet. Es wird dringend empfohlen, beim Warten auf Signale die Subroutine sigwait zu verwenden. Die Subroutine sigaction wird nicht empfohlen, da die Liste der Signalhandler auf Prozessebene verwaltet wird und jeder Thread innerhalb des Prozesses sie ändern kann. Wenn zwei Threads einen Signalhandler für dasselbe Signal festlegen, überschreibt der letzte Thread, der die Subroutine sigaction aufgerufen hat, die Einstellung des vorherigen Threadaufrufs. In den meisten Fällen kann die Reihenfolge, in der Threads terminiert werden, nicht vorhergesagt werden.
Signalmasken werden auf Threadebene verwaltet. Jeder Thread kann über eine eigene Gruppe von Signalen verfügen, die für die Zustellung blockiert werden. Die Subroutine sigthreadmask muss verwendet werden, um die Signalmaske des aufrufenden Threads abzurufen und festzulegen. Die Subroutine sigprocmask darf nicht in Multithread-Programmen verwendet werden, da ein unerwartetes Verhalten auftreten kann.
Die Subroutine pthread_sigmask ist der Subroutine sigprocmask sehr ähnlich. Die Parameter und die Verwendung beider Subroutinen sind identisch. Wenn Sie vorhandenen Code zur Unterstützung der Threadbibliothek portieren, können Sie die Subroutine sigprocmask durch die Subroutine pthread_sigmask ersetzen.
Signalgenerierung
Signale, die durch eine Aktion generiert werden, die auf einen bestimmten Thread zurückzuführen ist (z. B. Hardwarefehler), werden an den Thread gesendet, der die Generierung des Signals verursacht hat. Signale, die in Verbindung mit einer Prozess-ID, einer Prozessgruppen-ID oder einem asynchronen Ereignis (z. B. Terminalaktivität) generiert wurden, werden an den Prozess gesendet.
- Die Subroutine pthread_kill sendet ein Signal an einen Thread. Da Thread-IDs Threads innerhalb eines Prozesses identifizieren, kann diese Subroutine nur Signale an Threads innerhalb desselben Prozesses senden.
- Die Subroutine kill (und somit der Befehl kill ) sendet ein Signal an einen Prozess. Ein Thread kann ein Signal Signal an seinen Prozess senden, indem er den folgenden Aufruf ausführt:
kill(getpid(), Signal); - Die Subroutine raise kann nicht verwendet werden, um ein Signal an den Prozess des aufrufenden Threads zu senden. Die Subroutine raise sendet wie im folgenden Aufruf ein Signal an den aufrufenden Thread:
Dadurch wird sichergestellt, dass das Signal an den Aufrufenden der Subroutine raise gesendet wird. Daher können Bibliotheksroutinen, die für Einzelthreadprogramme geschrieben wurden, ohne großen Aufwand auf ein Multithread-System portiert werden, da die Subroutine raise normalerweise dazu bestimmt ist, das Signal an den Aufrufenden zu senden.pthread_kill(pthread_self(), Signal); - Die Subroutine alarm fordert an, dass ein Signal später an den Prozess gesendet wird, und die Alarmstatus werden auf Prozessebene verwaltet. Daher überschreibt der letzte Thread, der die Subroutine alarm aufgerufen hat, die Einstellungen anderer Threads im Prozess. In einem Multithread-Programm wird das Signal SIGALRM nicht unbedingt an den Thread übergeben, der die Subroutine alarm aufgerufen hat. Der aufrufende Thread wird möglicherweise sogar beendet und kann daher das Signal nicht empfangen.
Signale verarbeiten
- Signalhandler können die Subroutine Longjmp oder Siglongjmp nur aufrufen, wenn der entsprechende Aufruf der Subroutine Setjmp oder Sigsetjmp in demselben Thread ausgeführt wird.
Normalerweise installiert ein Programm, das auf ein Signal warten möchte, einen Signalhandler, der die Subroutine longjmp aufruft, um die Ausführung an dem Punkt fortzusetzen, an dem die entsprechende Subroutine setjmp aufgerufen wird. Dies ist in einem Multithread-Programm nicht möglich, weil das Signal möglicherweise an einen anderen Thread als den, der die Subroutine setjmp aufgerufen hat, übermittelt wird, wodurch der Handler vom falschen Thread ausgeführt wird.
Hinweis: Die Verwendung von longjmp aus einem Signalhandler kann zu nicht definiertem Verhalten führen. - Von einem Signalhandler können keine pthread -Routinen aufgerufen werden. Das Aufrufen einer pthread -Routine über einen Signalhandler kann zu einem Anwendungsdeadlock führen.
Damit ein Thread auf asynchron generierte Signale warten kann, stellt die Threadbibliothek die Subroutine sigwait bereit. Die Subroutine sigwait blockiert den aufrufenden Thread, bis eines der erwarteten Signale an den Prozess oder an den Thread gesendet wird. Unter Verwendung der Subroutine sigwait darf kein Signalhandler im erwarteten Signal installiert sein.
#include <pthread.h>
#include <signal.h>
static pthread_mutex_t mutex;
sigset_t set;
static int sig_cond = 0;
void *run_me(void *id)
{
int sig;
int err;
sigset_t sigs;
sigset_t oldSigSet;
sigfillset(&sigs);
sigthreadmask(SIG_BLOCK, &sigs, &oldSigSet);
err = sigwait(&set, &sig);
if(err)
{
/* do error code */
}
else
{
printf("SIGINT caught\n");
pthread_mutex_lock(&mutex);
sig_cond = 1;
pthread_mutex_unlock(&mutex);
}
return;
}
main()
{
pthread_t tid;
sigemptyset(&set);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, 0);
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid, NULL, run_me, (void *)1);
while(1)
{
sleep(1);
/* or so something here */
pthread_mutex_lock(&mutex);
if(sig_cond)
{
/* do exit stuff */
return;
}
pthread_mutex_unlock(&mutex);
}
}Wenn mehr als ein Thread die Subroutine sigwait aufgerufen hat, wird genau ein Aufruf zurückgegeben, wenn ein übereinstimmendes Signal gesendet wird. Welcher Thread aktiviert ist, kann nicht vorhergesagt werden. Wenn ein Thread sigwait ausführen und einige andere Signale verarbeiten wird, für die er sigwaitnicht ausführt, müssen die benutzerdefinierten Signalhandler die Sigwartersignale für die ordnungsgemäße Behandlung blockieren. Beachten Sie, dass die Subroutine sigwait einen Abbruchpunkt bereitstellt.
Da ein dedizierter Thread kein echter Signalhandler ist, kann er jedem anderen Thread eine Bedingung signalisieren. Es ist möglich, eine Routine sigwait_multiple zu implementieren, die alle Threads aktiviert, die auf ein bestimmtes Signal warten. Jeder Aufrufende der Routine sigwait_multiple registriert eine Gruppe von Signalen. Der Aufrufende wartet dann auf eine Bedingungsvariable. Ein Einzelthread ruft die Subroutine sigwait in der Union aller registrierten Signale auf. Wenn der Aufruf der Subroutine sigwait zurückgegeben wird, wird der entsprechende Status festgelegt und Bedingungsvariablen werden übertragen. Neue Aufrufende der Subroutine sigwait_multiple führen dazu, dass der anstehende Aufruf der Subroutine sigwait abgebrochen und erneut ausgegeben wird, um die Gruppe der Signale zu aktualisieren, auf die gewartet wird.
Signalübermittlung
Ein Signal wird an einen Thread übermittelt, sofern seine Aktion nicht auf 'ignore' gesetzt ist. Die folgenden Regeln regeln die Signalbereitstellung in einem Multithread-Prozess:
- Ein Signal, dessen Aktion auf Beenden, Stoppen oder Fortsetzen des Zielthreads bzw. -prozesses gesetzt ist, beendet, stoppt oder setzt den gesamten Prozess (und damit alle zugehörigen Threads) fort. Einzelthread-Programme können somit als Multithread-Programme umgeschrieben werden, ohne ihr extern sichtbares Signalverhalten zu ändern.
Stellen Sie sich beispielsweise einen Multithread-Benutzerbefehl wie den Befehl grep vor. Ein Benutzer kann den Befehl in seiner bevorzugten Shell starten und ihn dann stoppen, indem er ein Signal mit dem Befehl kill sendet. Das Signal sollte den gesamten Prozess stoppen, der den Befehl grep ausführt.
- Für einen bestimmten Thread generierte Signale, die pthread_kill oder die Subroutinen raise verwenden, werden an diesen Thread übergeben. Wenn der Thread das Signal für die Zustellung blockiert hat, wird das Signal auf den Thread gesetzt, bis das Signal für die Zustellung freigegeben wird. Wird der Thread vor der Signalübermittlung beendet, wird das Signal ignoriert.
- Für einen Prozess generierte Signale, z. B. mit der Subroutine kill , werden genau einem Thread im Prozess zugestellt. Wenn mindestens ein Thread die Subroutine sigwait aufgerufen hat, wird das Signal genau einem dieser Threads zugestellt. Andernfalls wird das Signal genau einem Thread zugeführt, der das Signal nicht von der Zustellung blockiert hat. Wenn kein Thread diese Bedingungen erfüllt, wird das Signal für den Prozess in den Wartestatus versetzt, bis ein Thread die Subroutine sigwait aufruft, die dieses Signal angibt, oder ein Thread die Zustellung des Signals aufhebt.
Wenn die Aktion, die einem anstehenden Signal (in einem Thread oder in einem Prozess) zugeordnet ist, auf Ignorieren gesetzt ist, wird das Signal ignoriert.