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.6 Die Attribute von Threads und das Schedulintoptop

Wie Sie bereits im Abschnitt zuvor erfahren haben, kann man auch das Attribut PTHREAD_CREATE_DETACHED zum Abhängen (detached) von Threads verwenden. Hierzu können die folgenden Funktionen verwendet werden:

#include <pthread. h.>
int pthread_attr_init( pthread_attr_t *attribute );
int pthread_attr_getdetachestate( pthread_attr_t *attribute,
                                  int detachstate );
int pthread_attr_setdetachestate( pthread_attr_t *attribute,
                                  int detachstate );
int pthread_attr_destroy( pthread_attr_t *attribute );

Mit der Funktion pthread_attr_init() müssen Sie zunächst das Attributobjekt attr initialisieren. Dabei werden auch gleich die voreingestellten Attribute gesetzt. Um beim Thema »detached« und »joinable« zu bleiben, ist die Voreinstellung hier PTHREAD_CREATE_JOINABLE, hiermit wird also der Thread nicht von den anderen losgelöst und erst freigegeben, wenn ein Thread nach dem Exit-Status diese Threads fragt (mit pthread_join()). Mit der Funktion pthread_attr_getdetachestate() können Sie das »detached«-Attribut erfragen, und mit pthread_attr_setdetachedstate() wird es gesetzt. Neben dem eben erwähnten PTHREAD_CREATE_JOINABLE, was ja auch die Standardeinstellung eines erzeugten Threads ist, können Sie hierbei auch PTHREAD_CREATE_DETACHED verwenden. Das Setzen von PTHREAD_CREATE_DETACHED entspricht exakt dem Verhalten der Funktion pthread_detach() (siehe Kap. 9.5.6) und kann auch stattdessen verwendet werden – da es erheblich kürzer ist. Benötigen Sie das Attributobjekt attr nicht mehr, können Sie es mit pthread_attr_destroy() löschen. Somit machen die Funktionen wohl erst Sinn, wenn Sie bereits mit pthread_detach() einen Thread ausgehängt haben und diesen eventuell wieder zurückholen (PTHREAD_CREATE_JOINABLE) müssen.

Bedeutend wichtiger im Zusammenhang mit den Attributen von Threads erscheint hier schon das Setzen der Prozessorzuteilung (Scheduling). Laut POSIX gibt es drei verschiedene solcher Prozesszuteilungen (Scheduling Policies):

gp  SCHED_OTHER – Die normale Priorität wie bei einem gewöhnlichen Prozess. Der Thread wird beendet, entweder wenn seine Zeit um ist und er wartet, bis er wieder am Zuge ist, oder wenn ein anderer Thread oder Prozess gestartet wurde, der mit einer höheren Priorität ausgestattet ist.
gp  Echtzeit (SCHED_FIFO) – Dies sind Echtzeitprozesse. Sie werden in jedem Fall SCHED_OTHER-Prozessen vorgezogen. Auch können sie nicht von normalen Prozessen unterbrochen werden. Es gibt drei Möglichkeiten, Echtzeitprozesse zu unterbrechen:
    a.) Er wandert in eine Warteschlange und wartet auf ein externes Ereignis.
       
    b.) Er verlässt freiwillig die CPU (z. B. mit sched_yield()).
       
    c.) Er wird von einem anderen Echtzeitprozess mit einer höheren Priorität verdrängt.
       
gp  Echtzeit (SCHED_RR) – Dies sind Round-Robin-Echtzeitprozesse. Beim Round-Robin-Verfahren hat jeder Prozess die gleiche Zeitspanne zur Verfügung. Ist diese verstrichen, so kommt der nächste Prozess an die Reihe. Unter Linux werden diese Prozesse genauso behandelt wie die Echtzeitprozesse, mit dem Unterschied, dass diese an das Ende der run-queue gesetzt werden, wenn sie den Prozessor verlassen.

Jetzt habe ich hier Echtzeitoperationen ins Spiel geworfen und sollte daher hierzu einen kurzen Exkurs machen, damit man die Echtzeitstrategie nicht mit »jetzt – gleich sofort« vergleicht. Die Abarbeitung von Daten in der Echtzeit kann einfach nicht sofort ausgeführt werden, sondern auch hier muss man sich damit begnügen, dass diese innerhalb einer vorgegebenen Zeitspanne abgearbeitet werden. Allerdings müssen solche Echtzeitoperationen auch unterbrechbar sein, um auf plötzliche unvorsehbare Ereignisse reagieren zu können. Daher unterscheidet man hier zwischen »weichen« und »harten« Echtzeitanforderungen. Die Anforderungen hängen vom Anwendungsfall ab, so kann man bei einem Computerspiel jederzeit »weiche« Echtzeitanforderungen setzen – was bei Maschinenanforderungen wohl eher katastrophal sein kann. Hier muss innerhalb einer vorgegebenen Zeit reagiert werden. Der Hauptbereich von Echtzeitanwendungen ist immer noch:

gp  Multimedia - Audio, Video
gp  Steuerung, Regelung - Maschinen-, Robotersteuerung

Damit eine solche Zuteilungsstrategie auch funktioniert, muss das System diese auch unterstützen. Dies ist gegeben, wenn bei Ihnen die Konstante _POSIX_THREAD_PRIORITY_SCHEDULING definiert ist. Beachten Sie außerdem, dass die Echtzeit-Zuteilungsstrategien SCHED_FIFO und SCHED_RR nur vom Superuser root ausgeführt werden können.

Verändern bzw. erfragen der Zustellungsstrategie können Sie mit den folgenden Funktionen:

int pthread_setschedparam( pthread thread, int policy,
                           const struct sched_param *param);
int pthread_getschedparam( pthread thread, int policy,
                           struct sched_param *param);

Mit diesen Funktionen setzen (set) oder ermitteln (get) Sie die Zustellungsstrategie eines Threads mit der ID thread vom Typ pthread_t. Die Strategie legen Sie mit dem Parameter policy fest. Hierbei kommen die bereits beschriebenen Konstanten SCHED_OTHER, SCHED_FIFO und SCHED_RR in Frage. Mit dem letzten Parameter der Struktur sched_param, die sich in der Headerdatei <bits/sched. h.> befindet:

/* Struktur sched_param */
struct sched_param {
    int sched_priority;
};

legen Sie die gewünschte Priorität fest.

Das folgende Beispiel soll Ihnen zeigen, wie einfach es ist, die Zuteilungsstrategie und die Priorität zu verändern. Sie finden hierbei zwei Funktionen, eine, womit Sie die Strategie und Priorität abfragen können, und eine weitere, womit Sie diese Werte neu setzen können. Allerdings benötigen Sie für das Setzen Superuser-root-Rechte, was im Beispiel ebenfalls ermittelt wird.

/* thread4.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd. h.>
#include <pthread. h.>
#define MAX_THREADS 3
#define BUF 255
/* Funktion ermittelt die Zuteilungsstrategie *
 * und Priorität eines Threads */
static void getprio( pthread_t id ) {
   int policy;
   struct sched_param param;
   printf("\t->Thread %ld: ", id);
   if((pthread_getschedparam(id, &policy, &param)) == 0  ) {
      printf("Zuteilung: ");
      switch( policy ) {
         case SCHED_OTHER :  printf("SCHED_OTHER; "); break;
         case SCHED_FIFO  :  printf("SCHED_FIFO; ");  break;
         case SCHED_RR    :  printf("SCHED_RR; ");    break;
         default          :  printf("Unbekannt; ");  break;
      }
      printf("Priorität: %d\n", param.sched_priority);
   }
}
/* Funktion zum Setzen der Zuteilungsstrategie *
 * und Prioriät eines Threads */
static void setprio( pthread_t id, int policy, int prio ) {
   struct sched_param param;
   param.sched_priority=prio;
   if((pthread_setschedparam( pthread_self(),
                              policy, &param)) != 0  ) {
      printf("Konnte Zuteilungsstrategie nicht ändern\n");
      pthread_exit((void *)pthread_self());
   }
}
static void thread_prio_demo(void *name) {
   int policy;
   struct sched_param param;
   /*Aktuelle Zuteilungsstrategie und Priorität erfragen */
   getprio(pthread_self());
   /* Ändern darf hier nur der root */
   if( getuid() != 0 ) {
      printf("Verändern geht nur mit Superuser-Rechten\n");
      pthread_exit((void *)pthread_self());
   }
   /* Neue Zuteilungsstrategie und Priorität festsetzen */
   setprio(pthread_self(), SCHED_RR, 2);
   /* Nochmals abfragen, ob erfolgreich verändert ... */
   getprio(pthread_self());
   /* Thread-Ende */
   pthread_exit((void *)pthread_self());
}
int main (void) {
   int i;
   static int ret[MAX_THREADS];
   static pthread_t th[MAX_THREADS];
   printf("->Haupt-Thread (ID:%ld) gestartet ...\n",
      pthread_self());
   /* Threads erzeugen */
   for (i = 0; i < MAX_THREADS; i++) {
      if (pthread_create ( &th[i],NULL, &thread_prio_demo,
                           NULL) != 0) {
         printf ("Konnte keinen Thread erzeugen\n");
         exit (EXIT_FAILURE);
      }
   }
   /* Auf die Threads warten */
   for (i = 0; i < MAX_THREADS; i++)
      pthread_join (th[i], &ret[i]);
   /* Rückgabe der Threads auswerten */
   for (i = 0; i < MAX_THREADS; i++)
      printf("\t<-Thread %ld mit Arbeit fertig\n", ret[i]);
   printf("->Haupt-Thread (ID:%ld) fertig ...\n",
      pthread_self());
   return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o thread4 thread4.c -lpthread
$ ./thread4
->Haupt-Thread (ID:-1209412512) gestartet...
        ->Thread -1209414736: Zuteilung: SCHED_OTHER; Priorität: 0
!!! Verändern geht nur mit Superuser-Rechten!!!
        ->Thread -1217807440: Zuteilung: SCHED_OTHER; Priorität: 0
!!! Verändern geht nur mit Superuser-Rechten!!!
        ->Thread -1226200144: Zuteilung: SCHED_OTHER; Priorität: 0
!!! Verändern geht nur mit Superuser-Rechten!!!
        <-Thread -1209414736 mit Arbeit fertig
        <-Thread -1217807440 mit Arbeit fertig
        <-Thread -1226200144 mit Arbeit fertig
->Haupt-Thread (ID:-1209412512) fertig ...
$ su
Password:********
# ./thread4
->Haupt-Thread (ID:-1209412512) gestartet ...
->Thread -1209414736: Zuteilung: SCHED_OTHER; Priorität: 0
->Thread -1209414736: Zuteilung: SCHED_RR; Priorität: 2
->Thread -1217807440: Zuteilung: SCHED_OTHER; Priorität: 0
->Thread -1217807440: Zuteilung: SCHED_RR; Priorität: 2
->Thread -1226200144: Zuteilung: SCHED_OTHER; Priorität: 0
->Thread -1226200144: Zuteilung: SCHED_RR; Priorität: 2
        <-Thread -1209414736 mit Arbeit fertig
        <-Thread -1217807440 mit Arbeit fertig
        <-Thread -1226200144 mit Arbeit fertig
->Haupt-Thread (ID:-1209412512) fertig ...

Selbiges (Zuteilungsstrategien und Priorität) können Sie übrigens mit folgenden Funktionen auch über Attributobjekte (pthread_attr_t) setzen bzw. erfragen:

#include <pthread. h.>
/* Zuteilungsstrategie verändern bzw. erfragen */
int pthread_attr_setschedpolicy( pthread_attr_t *attr,
                                 int policy);
int pthread_attr_getschedpolicy( const pthread_attr_t *attr,
                                 int *policy);
/* Priorität verändern bzw. erfragen */
int pthread_attr_setschedparam(
   pthread_attr_t *attr, const struct sched_param *param );
int pthread_attr_getschedparam(
   const pthread_attr_t *attr, struct sched_param *param );

Wollen Sie außerdem festlegen bzw. abfragen, wie ein Thread seine Attribute (Zuteilungsstrategie und Priorität) vom Erzeuger-Thread übernehmen soll, stehen Ihnen folgende Funktionen zur Verfügung:

#include <pthread. h.> 
int pthread_attr_setinheritsched(
   pthread_attr_t *attr, int inheritsched );
int pthread_attr_getinheritsched(
   const pthread_attr_t *attr, int *inheritsched );

Mit den beiden Funktionen phtread_attr_getinheritsched() und phtread_attr_setinheritsched() können Sie abfragen bzw. festlegen, wie der Thread die Attribute vom »Eltern«-Thread übernimmt. Dabei gibt es zwei Möglichkeiten – PTHREAD_INHERIT_SCHED, was bedeutet, dass der Kind-Thread die Attribute (mitsamt Zuteilungsstrategie und der Priorität) des Eltern-Threads übernimmt, und PTHREAD_EXPLICIT_SCHED bedeutet, eben nichts zu übernehmen, sondern das zu verwenden, was in attr als Zuteilungsstrategie und Priorität festgelegt ist. Wurden die Attribute des »Eltern«-Threads nicht verändert, so ist der Kind-Thread dennoch (logischerweise) mit denselben Attributen wie der »Eltern«-Thread ausgestattet – da Standardattribute.

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
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

Cookie-Einstellungen ändern