Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 10 Threads
  gp 10.1 Unterschiede zwischen Threads und Prozessen
  gp 10.2 Thread-Bibliotheken
  gp 10.3 Kernel- und User-Threads
  gp 10.4 Scheduling und Zustände von Threads
  gp 10.5 Die grundlegenden Funktionen zur Thread–Programmierung
    gp 10.5.1 pthread_create – einen neuen Thread erzeugen
    gp 10.5.2 pthread_exit – einen Thread beenden
    gp 10.5.3 pthread_join – auf das Ende eines Threads warten
    gp 10.5.4 pthread_self – die ID von Threads ermitteln
    gp 10.5.5 pthread_equal – die ID von zwei Threads vergleichen
    gp 10.5.6 pthread_detach – einen Thread unabhängig machen
  gp 10.6 Die Attribute von Threads und das Scheduling
  gp 10.7 Threads synchronisieren
    gp 10.7.1 Mutexe
    gp 10.7.2 Condition-Variablen (Bedingungsvariablen)
    gp 10.7.3 Semaphore
    gp 10.7.4 Weitere Synchronisationstechniken im Überblick
  gp 10.8 Threads abbrechen (canceln)
  gp 10.9 Erzeugen von Thread-spezifischen Daten (TSD-Data)
  gp 10.10 pthread_once – Codeabschnitt einmal ausführen
  gp 10.11 Thread-safe (thread-sichere Funktionen)
  gp 10.12 Threads und Signale
  gp 10.13 Zusammenfassung und Ausblick


Rheinwerk Computing

10.12 Threads und Signaltoptop

Signale lassen sich auch mit den Threads realisieren, nur muss man hierbei Folgendes beachten:

gp  Signale, die von der Hardware gesendet werden, bekommt immer der Thread, der das Hardware-Signal gesendet hat.
gp  Jedem Thread kann eine eigene Signalmaske zugeordnet werden. Allerdings gelten Signale, die mit sigaction() eingerichtet wurden, prozessweit für alle Threads.

Zur Verwendung von Signalen mit den Threads werden folgende Funktionen benötigt:

#include <pthread. h.>
#include <signal.h>
int pthread_sigmask( int how, const sigset_t  *newmask,
                     sigset_t  *old_mask );
int pthread_kill( pthread_t thread, int signo );
int sigwait( const sigset_t *set, int *sig );
int sigwaitinfo( const sigset_t *set, siginfo_t *info );
int sigtimedwait( const  sigset_t  *set,  siginfo_t *info,
                  const struct timespec timeout );

Mit der Funktion pthread_sigmask() können Sie eine Thread-Signalmaske erfragen oder ändern. Im Prinzip entspricht diese Funktion der von sigprocmask(), nur eben auf Threads und nicht Prozesse bezogen. Abgesehen von den Signalen SIGKILL und SIGSTOP können Sie auch hierzu alle bekannten Signale verwenden. Schlägt die Funktion pthread_sigmask() fehl, wird die Signalmaske des Threads nicht verändert.

Ich empfehle Ihnen, für die Funktion pthread_sigmask() das Kapitel der Signale nochmals (falls nötig) durchzulesen – da das Prinzip ähnlich wie zwischen den Prozessen funktioniert. Als erstes Argument für how wird eine Angabe erwartet, wie Sie die Signale verändern wollen. Mögliche Konstanten hierfür sind SIG_BLOCK, SIG_UNBLOCK und SIG_SETMASK. Als zweites Argument ist ein Zeiger auf einen Satz von Signalen nötig, der die aktuelle Signalmaske ergänzt, sie entfernt oder die Signalmaske ganz übernimmt. Hierfür kann auch NULL angegeben werden. Der dritte Parameter ist ein Zeiger auf die aktuelle Signalmaske. Hiermit können Sie entweder die aktuelle Signalmaske abfragen oder, wenn Sie mit dem zweiten Parameter eine neue Signalmaske einrichten, die alte Signalmaske sichern. Aber auch der dritte Parameter kann NULL sein. Wenn ein Thread einen weiteren Thread erzeugt, erbt dieser ebenfalls die Signalmaske. Wollen Sie also, dass alle Threads diese Signalmaske erben, sollten Sie vor der Erzeugung der Threads im Haupt-Thread die Signalmaske setzen.

Mit der Funktion pthread_kill() senden Sie dem Thread thread das Signal signo. Hierbei sollte man noch die Besonderheiten mit den Signalen SIGKILL, SIGTERM und SIGSTOP erläutern. Diese drei Signale gelten weiterhin prozessweit – senden Sie z. B. mit phtread_kill() das Signal SIGKILL an einen Thread, wird der komplette Prozess beendet, nicht nur der Thread. Ebenso sieht dies mit dem Signal SIGSTOP aus – hier wird der ganze Prozess (mit allen laufenden Threads) angehalten, bis ein anderer Prozess (nicht Thread) SIGCONT an den angehaltenen Prozess sendet.

Mit sigwait() halten Sie einen Thread so lange an, bis eines der Signale aus der Menge set gesendet wird. Die Signalnummer wird noch in sig geschrieben, bevor der Thread seine Ausführung fortsetzt. Wurde dem Signal ein Signalhandler zugeteilt, wird nichts in sig geschrieben.

Hierzu ein einfaches Beispiel, das Ihnen die Verwendung von Signalen in Verbindung mit Threads demonstriert.

/* thread20 */
#include <stdio.h>
#include <pthread. h.>
#include <signal.h>
pthread_t tid2;
void int_handler(int dummy) {
   printf("SIGINT erhalten von TID(%d)\n", pthread_self());
}
void usr1_handler(int dummy) {
   printf("SIGUSR1 erhalten von TID(%d)\n", pthread_self());
}
void *thread_1(void *dummy) {
   int sig, status, *status_ptr = &status;
   sigset_t sigmask;
   /* Kein Signal blockieren - SIG_UNBLOCK */
   sigfillset(&sigmask);
   pthread_sigmask(SIG_UNBLOCK, &sigmask, (sigset_t *)0);
   sigwait(&sigmask, &sig);
   switch(sig) {
      case SIGINT: int_handler(sig); break;
      default   : break;
   }
   printf("TID(%d) sende SIGINT an %d\n",
      pthread_self(), tid2);
   /* blockiert von tid2 */
   pthread_kill(tid2, SIGINT);    
   printf("TID(%d) sende SIGUSR1 an %d\n",
      pthread_self(), tid2);
   /* nicht blockiert von tid2 */
   pthread_kill(tid2, SIGUSR1); 
   pthread_join(tid2, (void **)status_ptr);
   printf("TID(%d) Exit-Status = %d\n", tid2, status);  
   printf("TID(%d) wird beendet\n", pthread_self());
   pthread_exit((void *)NULL);
}
void *thread_2(void *dummy) {
   int sig;
   sigset_t sigmask;
   /* Alle Bits auf null setzen */
   sigemptyset(&sigmask);
   /* Signal SIGUSR1 nicht blockieren ... */
   sigaddset(&sigmask, SIGUSR1);  
   pthread_sigmask(SIG_UNBLOCK, &sigmask, (sigset_t *)0);
   sigwait(&sigmask, &sig);
   switch(sig) {
      case SIGUSR1: usr1_handler(sig); break;
      default     : break;
   }
   printf("TID(%d) wird beendet\n", pthread_self());
   pthread_exit((void *)99);
}
int main(void) {
   pthread_t tid1;
   pthread_attr_t attr_obj; 
   void *thread_1(void *), *thread_2(void *);
   sigset_t sigmask;
   struct sigaction action;
   /* Signalmaske einrichten - alle Signale im *
    * Haupt-Thread blockieren */
   sigfillset(&sigmask);       /* Alle Bits ein ...*/
   pthread_sigmask(SIG_BLOCK, &sigmask, (sigset_t *)0);
   /* Setup Signal-Handler für SIGINT & SIGUSR1 */
   action.sa_flags = 0;
   action.sa_handler = int_handler;
   sigaction(SIGINT, &action, (struct sigaction *)0);
   action.sa_handler = usr1_handler;
   sigaction(SIGUSR1, &action, (struct sigaction *)0);
   pthread_attr_init(&attr_obj);
   pthread_attr_setdetachstate( &attr_obj,
                                PTHREAD_CREATE_DETACHED );
   pthread_create(&tid1, &attr_obj, thread_1, (void *)NULL);
   printf("TID(%d) erzeugt\n", tid1);
                                     - 10 -
   pthread_attr_setdetachstate( &attr_obj,
                                PTHREAD_CREATE_JOINABLE);
   pthread_create(&tid2, &attr_obj, thread_2, (void *)NULL);
   printf("TID(%d) erzeugt\n", tid2);
   sleep(1); // Kurze Pause ...
   printf("Haupt-Thread(%d) sendet SIGINT an TID(%d)\n",
      pthread_self(), tid1);
   pthread_kill(tid1, SIGINT);
   printf("Haupt-Thread(%d) sendet SIGUSR1 an TID(%d)\n", 
      pthread_self(), tid1);
   pthread_kill(tid1, SIGUSR1);
   printf("Haupt-Thread(%d) wird beendet\n",
      pthread_self());
   // Beendet nicht den Prozess!!!
   pthread_exit((void *)NULL); 
}

Das Programm bei der Ausführung:

$ gcc -o thread20 thread20.c -lpthread
$ ./thread20
TID(-1209418832) erzeugt
TID(-1217815632) erzeugt
Haupt-Thread(-1209416608) sendet SIGINT an TID(-1209418832)
Haupt-Thread(-1209416608) sendet SIGUSR1 an TID(-1209418832)
Haupt-Thread(-1209416608) wird beendet
SIGUSR1 erhalten von TID(-1209418832)
SIGINT erhalten von TID(-1209418832)
TID(-1209418832) sende SIGINT an -1217815632
TID(-1209418832) sende SIGUSR1 an -1217815632
SIGUSR1 erhalten von TID(-1217815632)
TID(-1217815632) wird beendet
TID(-1217815632) Exit-Status = 99
TID(-1209418832) wird beendet

Dieses Beispiel beinhaltet drei Threads (inklusive dem Haupt-Thread). Im Haupt-Thread wird die Signalmaske eingerichtet, so dass alle Signale im Haupt-Thread blockiert (SIGBLOCK) werden. Als Nächstes werden Signalhandler für SIGINT und SIGUSR1 eingerichtet. »thread_1« wird von den Threads abgehängt erzeugt (detached), und »thread_2« wird nicht von den anderen Threads abgehängt. Dann werden im Haupt-Thread die Signale SIGINT und SIGUSR1 an den »thread_1« gesendet, und der Haupt-Thread beendet sich.

»thread_1« (da abgehängt, gerne auch Daemon-Thread) hebt die Blockierung der Signale auf (SIG_UNBLOCK) und wartet auf Signale. Im Beispiel wurde ja bereits zuvor SIGINT und SIGUSR1 vom Haupt-Thread gesendet, was die Ausgabe des Signalhandlers auch bestätigt. Sobald also »thread_1« seine Signale bekommen hat, sendet es die Signale SIGINT und SIGUSR1 an »thread_2« und wartet (mittels pthread_join()), bis sich »thread_2« beendet, und gibt den Exit-Status von »thread_2« aus, bevor sich »thread_1« ebenfalls beendet.

»thread_2« hingegen hebt nur die Blockierung für SIGUSR1 auf – alle anderen Signale werden ja durch die Weitervererbung des Haupt-Threads weiterhin blockiert. Anschließend wartet »thread_2« auf das Signal. Trifft SIGUSR1 ein, wird der Signalhandler ausgeführt und der Thread mit einem Rückgabewert beendet (auf den »thread_1« ja mit pthread_join() wartet).

 << zurück
  
  Zum Katalog
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux-Server






 Linux-Server


Zum Katalog: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Katalog: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Katalog: Shell-Programmierung






 Shell-
 Programmierung


Zum Katalog: Linux Handbuch






 Linux Handbuch


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de