Signalbehandlung in pthreads

Lesezeit: 8 Minuten

Benutzer-Avatar
RajSanpui

Ich habe einen pthread erstellt und darin einen Signalhandler installiert, genauso wie wir es in tun main( ) Funktion. Der Signalhandler des Threads ist eine separate Funktion. Überraschenderweise funktioniert es nicht, das heißt, der Signalhandler des Threads kann keine Signale abfangen.

Hier ist der Code:

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 printf("Caught signal: %d\n",sig);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 printf("This is from thread function\n");
 signal(SIGSEGV,sig_func); // Register signal handler inside thread
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data *ptr;

 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 printf("Name:%s\n",ptr->name);
 printf("Age:%d\n",ptr->age);
}

Ausgabe:

Segmentierungsfehler (was bedeutet, dass das Signal nicht vom Handler abgefangen wird)

  • Sie haben festgestellt, dass Threads und Signale nicht gut interagieren 🙂

    – Sarnold

    12. März 2011 um 11:09 Uhr

  • Zunächst einmal und um fortzufahren, was @sarnold gesagt hat, verwenden Sie die falsche API. Verwende nicht signal(). Von der Manpage (Lies es): “Die Auswirkungen von signal() in einem Multithread-Prozess sind nicht spezifiziert.” Beginnen Sie mit dem Lesen von Dokumenten bei man 2 sigaction.

    – rlibby

    12. März 2011 um 11:16 Uhr

  • @rlibby: Soll ich also die “struct sigaction” oder “sigevent structure” verwenden, um die Signale abzufangen, meinst du?

    – RajSanpui

    12. März 2011 um 11:19 Uhr

Benutzer-Avatar
sam hocevar

Es gibt mehrere Probleme mit Ihrem Code:

  • ptr ist nicht initialisiert, also alle ptr-> Teile werden das Programm zum Absturz bringen
  • Du rufst an pthread_kill() sofort, sehr wahrscheinlich bevor der Signalhandler installiert wurde, und in einem Thread (der ein nicht spezifiziertes Verhalten hat)
  • du rufst an printf() von einem Signal-Handler, dessen Funktion nicht garantiert ist (siehe man 7 signal für eine Liste sicherer Funktionen)

Dies wird viel besser funktionieren, obwohl Sie immer noch eine ordnungsgemäße Thread-Synchronisierung benötigen, und wie an anderer Stelle erwähnt, sollten Sie verwenden sigaction():

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 write(1, "Caught signal 11\n", 17);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 fprintf(stderr, "This is from thread function\n");
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data d;
 data *ptr = &d;

 signal(SIGSEGV,sig_func); // Register signal handler before going multithread
 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 sleep(1); // Leave time for initialisation
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 fprintf(stderr, "Name:%s\n",ptr->name);
 fprintf(stderr, "Age:%d\n",ptr->age);
}

Bearbeiten: Sighandler im Hauptthread installieren

  • Vielen Dank, es hat funktioniert. Glaubst du also, dass nur die Pointer-Initialisierung ein Problem war? Aber wenn ich keine Signale liefere, funktioniert der Code ohne Fehler.

    – RajSanpui

    12. März 2011 um 11:30 Uhr

  • Auch das Registrieren des Signal-Handlers bei der Hauptfunktion anstelle der Thread-Funktion funktioniert ebenfalls. Wieso ist es so?

    – RajSanpui

    12. März 2011 um 11:31 Uhr

  • @kingsmasher: Die beiden Hauptprobleme waren die Zeigerinitialisierung und die Tatsache, dass Sie angerufen haben pthread_kill() bevor der untergeordnete Thread Zeit hatte, den Signalhandler festzulegen. Ohne diese beiden Korrekturen konnte Ihr Programm nicht wie erwartet ausgeführt werden. Die anderen Probleme sind Portabilitätsprobleme, die zu unerwartetem Verhalten auf verschiedenen Linux- oder Unix-Varianten führen und daher ebenfalls angegangen werden sollten. Bis dahin bedenken Sie, dass es nur zufällig funktioniert.

    – sam hocevar

    12. März 2011 um 11:40 Uhr

  • Ebenfalls erwähnenswert: meine Manpage für signal() sagt: “Die Auswirkungen von signal() in einem Multithread-Prozess sind nicht spezifiziert.” Und es empfiehlt die Verwendung sigaction() auch.

    – Julien-L

    9. April 2013 um 23:22 Uhr

  • ptr ist nicht initialisiert, also alle ptr-> Teile werden das Programm zum Absturz bringen absolut falsch, es hängt wirklich von vielen Dingen ab, aber es wird sicherlich dazu führen undefiniertes Verhalten.

    – Iharob Al Asimi

    2. September 2015 um 16:02 Uhr


Ich glaube, der Kern des Problems besteht darin, dass Signale an den Prozess als Ganzes geliefert werden und nicht an einzelne Threads. Üblicherweise wird ein einzelner Thread nominiert, um alle Signale zu verarbeiten; Alle anderen Threads (einschließlich des Hauptthreads) müssen dies tun Blockieren Sie die Signale mit pthread_sigmask().

Sie können die Maske so einstellen, dass alle Signale blockiert werden, Ihren Signal-Handler-Thread starten, die Signale demaskieren, die Sie verarbeiten möchten, und dann zurück im Haupt-Thread alle anderen Threads starten, die Sie benötigen. Sie erben die Maske „alle Signale blockieren“ vom Haupt-Thread.

Übrigens, es ist Zeit, wegzuziehen signal(3) und wechseln zu sigaction(2), die eine zuverlässige Semantik hat und besser standardisiert ist. (Und damit tragbarer.)

  • @sarnold: Sie sagten: “Glauben Sie, der Kern des Problems besteht darin, dass Signale an den gesamten Prozess und nicht an einzelne Threads geliefert werden”, aber wenn ich pthread_kill (thread_id, signal_no) verwende, wird das Signal meiner Meinung nach an den spezifischen Thread geliefert .

    – RajSanpui

    12. März 2011 um 11:23 Uhr

  • @Kingsmasher1, ich bin mir nicht sicher, ob das stimmt 🙂 aber @Sam Hocevars Antwort sieht fantastisch aus. Ich bin versucht, meine Antwort zu löschen, aber es könnte noch nützlich sein.

    – Sarnold

    12. März 2011 um 11:26 Uhr

  • @sarnold: Signale werden dem Prozess als Ganzes zugeführt, aber entsprechend der pthread_kill() Dokumentation, es stellt sicher, dass der erwartete Thread das Signal empfängt, wenn das Signal nicht empfangen wird SIGSTOP, SIGCONT oder SIGTERM.

    – sam hocevar

    12. März 2011 um 11:32 Uhr

  • @Sam Hocevar, alle Dokumente, die behaupteten, dass sie von den POSIX-Manpages stammten, die wunderbar sind, aber ich vertraue ihnen nicht, dass sie die Realität von Linux-Threading-APIs melden. 🙁 Wenn Sie Dokumente von den NPTL-Leuten haben, die dasselbe sagen, würde das meinen Tag wirklich erhellen. 🙂

    – Sarnold

    12. März 2011 um 11:35 Uhr

  • NPTL behauptet, POSIX-konform zu sein, und es macht einen ziemlich guten Job. Ich habe keine eklatanten Nichtkonformitätsprobleme gefunden, außer vielleicht einigen Dingen im Zusammenhang mit dem Beharren des Kernels darauf, separate real/effektive/gespeicherte/fs uid/gid und Gruppen pro Thread zu haben und den Benutzerbereich zu zwingen, sie alle zu synchronisieren. (NPTL synchronisiert keine Änderungen an den zusätzlichen Gruppen, obwohl POSIX angibt, dass die Gruppen eine Eigenschaft des Prozesses und nicht des Threads sind.)

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    12. März 2011 um 13:36 Uhr

Das einzige Problem mit Ihrem Code, das noch niemand erwähnt hat, ist, dass während der Signalblockierung (und Zustellung, falls Sie verwenden pthread_kill oder raise) sind pro Thread, Signalhandler sind pro Prozess. Dies bedeutet, dass sie ein sehr schlechter Mechanismus für die Kommunikation zwischen Threads sind, insbesondere wenn Ihr Code jemals als Bibliothekscode verwendet wird, da es für eine Bibliothek ein äußerst schlechtes Verhalten ist, die Signalhandler des Aufrufers zu ändern.

Beachten Sie auch, dass die Verwendung von Signal-Handlern für die Kommunikation zwischen Threads im Vergleich zu anderen Methoden der Thread-Signalisierung wie Bedingungsvariablen oder Barrieren eine suboptimale Leistung aufweist, da es mindestens einen zusätzlichen Benutzer-Kernel-Benutzer-Übergang gibt (wenn der Signal-Handler zurückkehrt).

  • Vielen Dank für die sorgfältige Untersuchung und Ihre Kommentare. Sie sagten: “Während die Signalblockierung (und die Zustellung, wenn Sie pthread_kill oder raise verwenden) pro Thread sind, sind Signalhandler pro Prozess. ” Wenn wir den Signalhandler innerhalb des Threads registrieren, denken Sie nicht, dass es sich um eine Referenz handelt nur zu diesem Thread? Was ist der Unterschied zwischen dem Hinzufügen von Signal-Handlern (genauer gesagt, Register-Signal-Handlern) innerhalb der Hauptfunktion und innerhalb von Threads? Ich weiß, wenn wir uns in main( ) registrieren, kümmert es sich auch um Threads, da Threads den main( )-Code teilen.

    – RajSanpui

    13. März 2011 um 10:34 Uhr


  • Wenn wir also den Handler innerhalb der Thread-Funktion registrieren, ist er nur für diesen Thread spezifisch?

    – RajSanpui

    13. März 2011 um 10:38 Uhr


  • @kingsmahser1: nein, nichts deutet darauf hin, dass der Speicher für signal Thread-lokal ist oder sein sollte. Sie sollten davon ausgehen, dass die Registrierung eines Signalhandlers unabhängig vom aktuellen Thread die gleiche Wirkung hat.

    – sam hocevar

    13. März 2011 um 20:31 Uhr

  • Wie Sam sagte, sind Signalhandler global verarbeiten. Sie können keine Thread-Signal-Handler installieren. Sie könnten versuchen, schlau zu sein und einen Signalhandler zu installieren, der einen Funktionszeiger mit liest pthread_getspecific als Thread-spezifischer Handler für das Signal, außer dass pthread_getspecific ist nicht async-signalsicher. Soweit ich weiß, gibt es keine asynchronsignalsichere Möglichkeit, um festzustellen, in welchem ​​​​Thread Sie sich befinden. Der einzige Punkt, an dem ein Signal an einen bestimmten Thread gesendet wird, ist das Generieren EINTR (wenn SA_RESTART wird weggelassen sigaction Flags) oder den Fortschritt dieses Threads aufhalten …

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    14. März 2011 um 2:19 Uhr

1368160cookie-checkSignalbehandlung in pthreads

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy