Upload
others
View
6
Download
0
Embed Size (px)
Citation preview
Jürgen Wolf
Linux-UNIX-ProgrammierungDas umfassende Handbuch
Auf einen Blick
1 Einführung ....................................................................................... 29
2 E/A-Funktionen ................................................................................ 43
3 Attribute von Dateien und Verzeichnissen ....................................... 115
4 Zugriff auf Systeminformationen ...................................................... 129
5 Devices – eine einfache Verbindung zur Hardware .......................... 149
6 System- und Benutzerdateien .......................................................... 169
7 Dämonen, Zombies und Prozesse .................................................... 187
8 Signale ............................................................................................. 255
9 IPC – Interprozesskommunikation ................................................... 275
10 Threads ............................................................................................ 349
11 Netzwerkprogrammierung ............................................................... 407
12 MySQL und PostgreSQL .................................................................. 519
13 Terminal E/A und Benutzerschnittstellen für die Konsole ................ 653
14 GTK+ ................................................................................................ 715
15 Übersicht über weitere beliebte GUI-Bibliotheken .......................... 841
16 Werkzeuge für Programmierer ......................................................... 859
17 Shellprogrammierung ....................................................................... 951
A Sicherheit unter Linux ...................................................................... 1067
B Funktionsreferenz ............................................................................ 1087
C Linux-Unix-Kommandoreferenz ....................................................... 1151
D Inhalt der Buch-CD .......................................................................... 1223
5
Inhalt
Vorwort des Autors ..................................................................................................... 23Vorwort der Fachgutachter .......................................................................................... 27
1 Einführung ........................................................................................... 29
1.1 Anforderung an den Leser ........................................................................ 291.2 Anforderung an das Betriebssystem .......................................................... 301.3 Von UNIX ... ............................................................................................. 301.4 ... zu Linux ............................................................................................... 321.5 Der Compiler GCC – eine kurze Einführung ............................................... 33
1.5.1 GCC erhöre uns – der Aufruf ...................................................... 331.5.2 Was befiehlst du – Meister? ....................................................... 341.5.3 Klassifikation der Dateitypen ..................................................... 37
1.6 POSIX, X/OPEN und ANSI C ..................................................................... 371.7 Übersicht zum Buch ................................................................................. 391.8 Schreibkonventionen ................................................................................ 411.9 Notationsstil ............................................................................................. 421.10 Weitere Hilfen .......................................................................................... 42
2 E/A-Funktionen ................................................................................... 43
2.1 Elementare E/A-Funktionen ...................................................................... 432.2 Filedeskriptor ........................................................................................... 44
2.2.1 Verwaltung für offene Deskriptoren ........................................... 452.3 Funktionen, die den Filedeskriptor verwenden ......................................... 46
2.3.1 Datei öffnen – open() ................................................................. 462.3.2 Anlegen einer neuen Datei – creat() ........................................... 512.3.3 Datei schließen – close() ............................................................ 522.3.4 Schreiben von Dateien – write() ................................................. 522.3.5 Lesen von Dateien – read() ........................................................ 552.3.6 Schreib-/Lesezeiger positionieren – lseek() ................................. 572.3.7 Duplizieren von Filedeskriptoren – dup() und dup2() ................. 592.3.8 Ändern oder Abfragen der Eigenschaften eines
Filedeskriptors – fcntl() .............................................................. 612.3.9 Record Locking – Sperren von Dateien einrichten ...................... 662.3.10 Multiplexing E/A – select() ......................................................... 752.3.11 Unterschiedliche Operationen – ioctl() ....................................... 782.3.12 Lesen und Schreiben mehrerer Puffer – writev() und readv() ...... 792.3.13 Übersicht zu weiteren Funktionen, die den
Filedeskriptor verwenden .......................................................... 80
Inhalt
6
2.4 Standard-E/A-Funktionen ......................................................................... 842.4.1 Der FILE-Zeiger .......................................................................... 842.4.2 Öffnen und Schließen von Dateien ............................................. 852.4.3 Formatierte Ausgabe .................................................................. 862.4.4 Formatierte Eingabe ................................................................... 872.4.5 Binäres Lesen und Schreiben ...................................................... 882.4.6 Zeichen- und zeilenweise Ein-/Ausgabe ..................................... 882.4.7 Status der Ein-/Ausgabe überprüfen ........................................... 892.4.8 Stream positionieren .................................................................. 902.4.9 Puffer kontrollieren .................................................................... 912.4.10 Datei löschen und umbenennen ................................................ 922.4.11 Temporäre Dateien erstellen ...................................................... 93
2.5 Mit Verzeichnissen arbeiten ..................................................................... 942.5.1 Ein neues Verzeichnis anlegen – mkdir() ..................................... 942.5.2 In ein Verzeichnis wechseln – chdir(), fchdir() ............................ 952.5.3 Ein leeres Verzeichnis löschen – rmdir() ..................................... 972.5.4 Format eines Datei-Eintrags in struct dirent ............................... 972.5.5 Einen Verzeichnisstream öffnen – opendir() ............................... 992.5.6 Lesen aus dem DIR-Stream – opendir()
und Schließen des DIR-Streams – closedir() ................................ 1002.5.7 Positionieren des DIR-Streams ................................................... 1032.5.8 Komplettes Verzeichnis einlesen – scandir() ............................... 1042.5.9 Ganze Verzeichnisbäume durchlaufen – ftw() ............................. 108
2.6 Fehlerbehandlung ..................................................................................... 1122.7 Ausblick ................................................................................................... 114
3 Attribute von Dateien und Verzeichnissen ......................................... 115
3.1 Struktur stat ............................................................................................. 1153.1.1 Dateiart und Zugriffsrechte einer Datei erfragen – st_mode ........ 1163.1.2 User-ID-Bit und Group-ID-Bit – st_uid und st_gid ...................... 1193.1.3 Inode ermitteln – st_ino ............................................................. 1203.1.4 Linkzähler – st_nlink .................................................................. 1203.1.5 Größe der Datei – st_size ........................................................... 1243.1.6 st_atime, st_mtime, st_ctime ...................................................... 125
4 Zugriff auf Systeminformationen ........................................................ 129
4.1 Informationen aus dem /proc-Verzeichnis herausziehen ........................... 1294.2 Hardware-/Systeminformationen ermitteln ............................................... 131
4.2.1 CPU-Informationen – /proc/cpuinfo ........................................... 1314.2.2 Geräteinformationen – /proc/devices ......................................... 1324.2.3 Speicherauslastung – /proc/meminfo .......................................... 1324.2.4 Weitere Hardware-Informationen zusammengefasst .................. 132
Inhalt
7
4.3 Prozessinformationen ............................................................................... 1354.3.1 /proc/$pid/cmdline .................................................................... 1364.3.2 /proc/$pid/environ .................................................................... 1374.3.3 /proc/self ................................................................................... 1374.3.4 /proc/$pid/fd/ ........................................................................... 1384.3.5 /proc/$pid/statm ....................................................................... 139
4.4 Kernel-Informationen ............................................................................... 1394.4.1 /proc/locks ................................................................................ 1454.4.2 /proc/modules ........................................................................... 145
4.5 Filesysteme ............................................................................................... 1464.5.1 /proc/mounts ............................................................................. 146
4.6 Weiterführendes ....................................................................................... 146
5 Devices – eine einfache Verbindung zur Hardware ............................. 149
5.1 Die Gerätedateitypen ............................................................................... 1495.2 Die Gerätedateinummern ......................................................................... 1505.3 Zugriff auf die Gerätedateien .................................................................... 1515.4 Gerätenamen ............................................................................................ 1525.5 Spezielle Gerätedateien ............................................................................ 1535.6 Gerätedateien in der Praxis einsetzen ....................................................... 154
5.6.1 CD auswerfen und wieder schließen .......................................... 1565.6.2 CD-ROM-Fähigkeiten ................................................................ 1575.6.3 Audio-CD abspielen – komplett und einzelne Tracks –
Pause, Fortfahren und Stopp ...................................................... 1575.6.4 Aktuellen Status der Audio-CD ermitteln ................................... 1615.6.5 Das komplette Listing ................................................................ 163
6 System- und Benutzerdateien ............................................................. 169
6.1 Die Datei /etc/passwd .............................................................................. 1696.1.1 Die Datei /etc/passwd auswerten ............................................... 1706.1.2 getpwuid und getpwnam – einzelne Abfrage von /etc/passwd ... 1716.1.3 getpwent, setpwent und endpwent – komplette Abfrage
von /etc/passwd ........................................................................ 1736.2 Die Datei /etc/shadow .............................................................................. 174
6.2.1 Die Datei /etc/shadow auswerten .............................................. 1756.2.2 getspent, setspent und endspent – komplette Abfrage
von /etc/shadow ........................................................................ 1766.3 Die Datei /etc/group ................................................................................ 179
6.3.1 Die Datei /etc/group auswerten ................................................. 1806.3.2 getgrnam und getgrgid – einzelne Einträge
aus /etc/group abfragen ............................................................. 180
Inhalt
8
6.3.3 getgrent, setgrent und endgrent – alle Einträge in /etc/group abfragen ................................................................... 182
6.4 uname – Informationen zum lokalen System erfragen ............................... 1836.5 Das Verzeichnis /etc/skel und Network Information Service (NIS) ............. 1846.6 Dateien für Netzwerkinformationen .......................................................... 184
7 Dämonen, Zombies und Prozesse ....................................................... 187
7.1 Was ist ein Prozess? .................................................................................. 1877.2 Prozesskomponente ................................................................................. 188
7.2.1 Prozessnummer (PID) ................................................................ 1897.2.2 Prozessnummer des Vaterprozesses (PPID) ................................ 1897.2.3 Benutzer- und Gruppennummer eines Prozesses
(UID, EUID, GID, EGID) ............................................................. 1897.2.4 Prozessstatus ............................................................................. 1907.2.5 Prozesspriorität .......................................................................... 1917.2.6 Timesharing-Prozesse ................................................................. 1917.2.7 Prozessauslagerung .................................................................... 1957.2.8 Steuerterminal ........................................................................... 195
7.3 Prozesse überwachen – ps, top, kpm ........................................................ 1967.4 Lebenszyklus eines Prozesses .................................................................... 1987.5 Umgebungsvariablen eines Prozesses ........................................................ 200
7.5.1 Einzelne Umgebungsvariablen abfragen ..................................... 2017.5.2 Umgebungsvariable verändern oder hinzufügen –
putenv() und setenv() ................................................................. 2027.5.3 Löschen von Umgebungsvariablen – unsetenv()
und clearenv() ............................................................................ 2057.6 Ressourcenlimits eines Prozesses .............................................................. 206
7.6.1 Mehr Sicherheit mit Ressourcenlimits ........................................ 2097.7 Prozesserkennung ..................................................................................... 2097.8 Erzeugung von Prozessen – fork() .............................................................. 211
7.8.1 Pufferung ................................................................................... 2147.8.2 Was wird vererbt und was nicht? ............................................... 2187.8.3 Einen Prozess mit veränderter Priorität erzeugen ....................... 218
7.9 Warten auf einen Prozess ......................................................................... 2207.10 Die exec-Familie ....................................................................................... 226
7.10.1 execl() ........................................................................................ 2277.10.2 execve() ..................................................................................... 2287.10.3 execv() ....................................................................................... 2287.10.4 execle() ...................................................................................... 2297.10.5 execlp() ...................................................................................... 2297.10.6 execvp() ..................................................................................... 2297.10.7 Kindprozesse mit exec-Aufruf überlagern ................................... 230
Inhalt
9
7.11 Kommandoaufrufe aus dem Programm – system() .................................... 2317.12 Dämonprozesse ........................................................................................ 232
7.12.1 Wie ein Prozess zum Dämon wird ... .......................................... 2337.12.2 Dämon, sprich mit uns ... ........................................................... 2337.12.3 Protokollieren von Dämonen – syslog() ...................................... 2347.12.4 syslog() in der Praxis .................................................................. 2367.12.5 Den Dämon, den ich rief ... ........................................................ 238
7.13 Rund um die Ausführung von Prozessen ................................................... 2427.13.1 Einen Dämon beim Booten mit einem init-Skript starten ........... 2427.13.2 Hintergrundprozesse und Jobkontrolle ....................................... 2497.13.3 Prozesse zeitgesteuert ausführen (cron-Jobs) .............................. 252
7.14 Zusammenfassung und Ausblick ............................................................... 254
8 Signale ................................................................................................. 255
8.1 Grundlage zu den Signalen ....................................................................... 2558.1.1 Signalmaske ............................................................................... 2578.1.2 Signale und fork() ...................................................................... 2578.1.3 Signale und exec ........................................................................ 2588.1.4 Übersicht zu den Signalen .......................................................... 258
8.2 Das neue Signalkonzept ............................................................................ 2608.2.1 Wozu ein »neues« Signalkonzept? .............................................. 260
8.3 Signalmenge initialisieren ......................................................................... 2608.4 Signalmenge hinzufügen oder löschen ...................................................... 2618.5 Signale einrichten oder erfragen ............................................................... 261
8.5.1 Einen Signalhandler einrichten, der zurückkehrt ......................... 2658.6 Signal an den eigenen Prozess senden – raise() ......................................... 2678.7 Signale an andere Prozesse senden – kill() ................................................. 2678.8 Zeitschaltuhr einrichten – alarm() ............................................................. 2688.9 Prozesse stoppen, bis ein Signal eintritt – pause() ..................................... 2688.10 Prozesse für eine bestimmte Zeit stoppen –
sleep() und usleep() .................................................................................. 2698.11 Signalmaske erfragen oder ändern – sigprocmask() ................................... 2698.12 Prozess während einer Änderung der Signalmaske stoppen –
sigsuspend() ............................................................................................. 2708.13 Prozesse synchronisieren .......................................................................... 271
9 IPC – Interprozesskommunikation ...................................................... 275
9.1 Unterschiedliche Interprozesskommunikations-Techniken im Überblick ............................................................................................. 2769.1.1 (Namenlose) Pipes ..................................................................... 2769.1.2 Benannte Pipes (FIFO-Pipes) ...................................................... 277
Inhalt
10
9.1.3 Message Queue (Nachrichtenspeicher) ...................................... 2789.1.4 Semaphore ................................................................................ 2799.1.5 Shared Memory (gemeinsamer Speicher) ................................... 2799.1.6 STREAMS .................................................................................. 2809.1.7 Sockets ...................................................................................... 2819.1.8 Lock Files (Sperrdateien) ............................................................ 2819.1.9 Dateisperren (Record Locking) ................................................... 281
9.2 Gründe für IPC ......................................................................................... 2829.3 Pipes ........................................................................................................ 283
9.3.1 Eigenschaften von Pipes ............................................................. 2839.3.2 Pipes einrichten – pipe() ............................................................ 2839.3.3 Eigenschaften von elementaren E/A-Funktionen bei Pipes ......... 2879.3.4 Standard-E/A-Funktionen mit pipe ............................................ 2879.3.5 Pipes in einen anderen Prozess umleiten .................................... 2899.3.6 Filterprogramm erstellen mithilfe einer Pipe .............................. 2929.3.7 Einrichten einer Pipe zu einem anderen Prozess – popen() ......... 2959.3.8 Mail versenden mit Pipes und Sendmail ..................................... 2969.3.9 Drucken über eine Pipe mit lpr .................................................. 2999.3.10 Benannte Pipes – FIFOs ............................................................. 303
9.4 System-V-Interprozesskommunikation ...................................................... 3189.4.1 Gemeinsamkeiten der SysV-Mechanismen ................................. 3189.4.2 Ein Objekt einrichten, eine Verbindung herstellen
und das Objekt wieder löschen .................................................. 3209.4.3 Datenaustausch zwischen nicht verwandten Prozessen .............. 320
9.5 Semaphore ............................................................................................... 3219.5.1 Lebenszyklus eines Semaphors ................................................... 3219.5.2 Ein Semaphor öffnen oder erstellen – semget() .......................... 3249.5.3 Abfragen, ändern oder löschen der Semaphormenge –
semctl() ..................................................................................... 3259.5.4 Operationen auf Semaphormengen – semop() ............................ 3269.5.5 Semaphore im Vergleich mit Sperren ......................................... 328
9.6 Message Queues ...................................................................................... 3289.6.1 Eine Message Queue öffnen oder erzeugen – msgget() ............... 3299.6.2 Nachrichten versenden – msgsnd() ............................................. 3299.6.3 Eine Nachricht empfangen – msgrcv() ........................................ 3309.6.4 Abfragen, ändern oder löschen einer Message Queue –
msgctl() ..................................................................................... 3319.7 Shared Memory ........................................................................................ 339
9.7.1 Ein Shared-Memory-Segment erstellen oder öffnen – shmget() .................................................................................... 339
9.7.2 Ein Shared-Memory-Segment abfragen, ändern oder löschen – shmctl() ...................................................................... 340
Inhalt
11
9.7.3 Ein Shared-Memory-Segment anbinden (attach) – shmat() ......... 3419.7.4 Ein Shared-Memory-Segment loslösen – shmdt() ....................... 3419.7.5 Client-Server-Beispiel – Shared Memory .................................... 342
10 Threads ................................................................................................ 349
10.1 Unterschiede zwischen Threads und Prozessen ......................................... 34910.2 Thread-Bibliotheken ................................................................................. 35010.3 Kernel- und User-Threads ......................................................................... 35110.4 Scheduling und Zustände von Threads ...................................................... 35110.5 Die grundlegenden Funktionen zur Thread-Programmierung .................... 352
10.5.1 pthread_create – einen neuen Thread erzeugen ......................... 35210.5.2 pthread_exit – einen Thread beenden ........................................ 35310.5.3 pthread_join – auf das Ende eines Threads warten ..................... 35410.5.4 pthread_self – die ID von Threads ermitteln ............................... 35410.5.5 pthread_equal – die ID von zwei Threads vergleichen ................ 35910.5.6 pthread_detach – einen Thread unabhängig machen .................. 361
10.6 Die Attribute von Threads und das Scheduling ......................................... 36210.7 Threads synchronisieren ........................................................................... 367
10.7.1 Mutexe ...................................................................................... 37010.7.2 Condition-Variablen (Bedingungsvariablen) ............................... 37710.7.3 Semaphore ................................................................................ 38710.7.4 Weitere Synchronisationstechniken im Überblick ....................... 390
10.8 Threads abbrechen (canceln) .................................................................... 39110.9 Erzeugen von Thread-spezifischen Daten (TSD-Data) ................................ 39510.10 pthread_once – Codeabschnitt einmal ausführen ...................................... 39810.11 Thread-safe (thread-sichere Funktionen) ................................................... 40010.12 Threads und Signale ................................................................................. 40110.13 Zusammenfassung und Ausblick ............................................................... 405
11 Netzwerkprogrammierung .................................................................. 407
11.1 Einführung ............................................................................................... 40711.2 Aufbau von Netzwerken ........................................................................... 408
11.2.1 ISO/OSI und TCP/IP – Referenzmodell ....................................... 40811.2.2 Das World Wide Web (Internet) ................................................ 410
11.3 TCP/IP – Aufbau und Struktur ................................................................... 41111.3.1 Netzwerkschicht (Datenübertragung) ......................................... 41211.3.2 Internetschicht .......................................................................... 41211.3.3 Transportschicht (TCP, UDP) ...................................................... 41211.3.4 Anwendungsschicht ................................................................... 413
11.4 TCP Socket ............................................................................................... 41411.5 Kommunikationsmodell ............................................................................ 415
Inhalt
12
11.6 Grundlegende Funktionen zum Zugriff auf die Socket-Schnittstelle ........... 41611.6.1 Ein Socket anlegen – socket() ..................................................... 41611.6.2 Verbindungsaufbau – connect() .................................................. 41811.6.3 Socket mit einer Adresse verknüpfen – bind() ............................ 42011.6.4 Auf Verbindungen warten – listen() und accept() ....................... 42111.6.5 Senden und empfangen von Daten (1) – write() und read() ........ 42211.6.6 Senden und empfangen von Daten (2) – send() und recv() ......... 42211.6.7 Verbindung schließen – close() ................................................... 423
11.7 Aufbau eines Clientprogramms ................................................................. 42411.7.1 Zusammenfassung Clientanwendung und Quellcode .................. 426
11.8 Aufbau des Serverprogramms ................................................................... 42711.8.1 Zusammenfassung: Serveranwendung und Quellcode ............... 429
11.9 IP-Adressen konvertieren, manipulieren und extrahieren .......................... 43211.9.1 inet_aton(), inet_pton() und inet_addr() ..................................... 43211.9.2 inet_ntoa() und inet_ntop() ........................................................ 43311.9.3 inet_network() ........................................................................... 43411.9.4 inet_netof() ................................................................................ 43511.9.5 inet_lnaof() ................................................................................ 43511.9.6 inet_makeaddr() ........................................................................ 435
11.10 Namen und IP-Adressen umwandeln ........................................................ 43811.10.1 Name-Server .............................................................................. 43911.10.2 Informationen zum Rechner im Netz – gethostbyname
und gethostbyaddr .................................................................... 43911.10.3 Service-Informationen – getservbyname() und getservbyport() ... 443
11.11 Der Puffer ................................................................................................. 44611.12 Standard-E/A-Funktionen verwenden ....................................................... 447
11.12.1 Pufferung von Standard-E/A-Funktionen .................................... 44811.13 Parallele Server ......................................................................................... 44811.14 Syncrones Multiplexing – select() .............................................................. 46211.15 POSIX-Threads und Netzwerkprogrammierung ......................................... 48111.16 Optionen für Sockets setzen bzw. erfragen ............................................... 485
11.16.1 setsockopt() ............................................................................... 48611.16.2 getsockopt() .............................................................................. 48611.16.3 Socket-Optionen ....................................................................... 486
11.17 UDP ......................................................................................................... 48911.17.1 Clientanwendung ....................................................................... 49111.17.2 Serveranwendung ...................................................................... 49211.17.3 recvfrom() und sendto() ............................................................. 49211.17.4 bind() verwenden oder weglassen .............................................. 496
11.18 Unix-Domain-Sockets (IPC) ...................................................................... 49611.18.1 Die Adressstruktur von Unix-Domain-Sockets ............................ 49711.18.2 Lokale Sockets erzeugen – socketpair() ....................................... 500
Inhalt
13
11.19 Multicast-Socket ...................................................................................... 50211.19.1 Anwendungsgebiete von Multicast-Verbindungen ..................... 509
11.20 Nicht blockierende I/O-Sockets ................................................................ 50911.21 Etwas zu Streams und TLI, Raw Socket, XTI .............................................. 512
11.21.1 Raw Socket ................................................................................ 51211.21.2 TLI und XTI ................................................................................ 51311.21.3 RPC (Remote Procedure Call) ..................................................... 513
11.22 IPv4 und IPv6 ........................................................................................... 51411.22.1 IPv6 – ein wenig genauer ........................................................... 515
11.23 Netzwerksoftware nach IPv6 portieren ..................................................... 51511.23.1 Konstanten ................................................................................ 51511.23.2 Strukturen ................................................................................. 51611.23.3 Funktionen ................................................................................ 516
11.24 Sicherheit und Verschlüsselung ................................................................. 517
12 MySQL und PostgreSQL ...................................................................... 519
12.1 Relationales Datenbanksystem ................................................................. 51912.2 Relationaler Datenbankserver ................................................................... 52112.3 SQL-Server im Überblick ........................................................................... 52212.4 MySQL ..................................................................................................... 522
12.4.1 Anwendungsgebiete von MySQL ............................................... 52312.4.2 Schnittstellen von MySQL .......................................................... 52412.4.3 Installation von MySQL ............................................................. 52412.4.4 MySQL-Server starten und stoppen ........................................... 52412.4.5 Konfigurationsdatei my.cnf ........................................................ 52512.4.6 Kommandozeilenwerkzeuge für und von mysql ......................... 52612.4.7 Grafische Clients ........................................................................ 53012.4.8 MySQL-Crashkurs ...................................................................... 53012.4.9 Datentypen ............................................................................... 53112.4.10 Datenbank anlegen, verwenden und löschen ............................. 53412.4.11 Tabelle anlegen ......................................................................... 53512.4.12 Schlüsselfelder (Tabellen anlegen) .............................................. 53612.4.13 Indices ....................................................................................... 53712.4.14 Tabellentypen (Tabellen anlegen) ............................................... 53812.4.15 Autowerte definieren ................................................................. 53812.4.16 Tabellen umbenennen und ändern ............................................. 53812.4.17 Daten einfügen, ändern und löschen .......................................... 54012.4.18 Daten importieren ..................................................................... 54212.4.19 Datenausgabe ............................................................................ 54312.4.20 NULL ist 0 oder undefiniert? ...................................................... 54512.4.21 Unscharfe Suche ........................................................................ 545
Inhalt
14
12.5 MySQL C-API ........................................................................................... 54612.5.1 Verbindung mit dem MySQL-Server aufbauen ........................... 54712.5.2 Aufgetretene Fehler ermitteln – mysql_errno() und
mysql_error() ............................................................................. 54912.5.3 Schließt die Verbindung zum Server – mysql_close() .................. 55012.5.4 Erstes Beispiel ............................................................................ 55012.5.5 Verschiedene Informationen ermitteln ....................................... 55512.5.6 Datenbanken, Tabellen und Felder ausgeben (MYSQL_RES) ....... 55812.5.7 Ergebnismenge zeilenweise bearbeiten (MYSQL_ROW) ............. 56012.5.8 Ergebnismenge spaltenweise einlesen (und ausgeben)
(MYSQL_FIELD) ......................................................................... 56212.5.9 Ein Beispiel ................................................................................ 56612.5.10 Ergebnismenge – weitere Funktionen ......................................... 57212.5.11 Befehle an den Server – mysql_query() und
mysql_real_query() ..................................................................... 57312.5.12 Weitere Funktionen ................................................................... 57712.5.13 Veraltete Funktionen ................................................................. 582
12.6 Beispiel eines Newssystems mit MySQL .................................................... 58312.6.1 Die Headerdatei my_cgi.h .......................................................... 58412.6.2 (Pseudo-)Planung ...................................................................... 58912.6.3 Datenbank und Tabellen anlegen ............................................... 58912.6.4 MySQL-Clients mit GUI ............................................................. 60812.6.5 Randnotiz .................................................................................. 609
12.7 Neue SQL-Funktionen für die Shell – MySQL erweitern ............................ 60912.8 MySQL-Funktionen mit der UDF-Schnittstelle entwerfen ......................... 610
12.8.1 UDF-Sequenzen ......................................................................... 61212.8.2 UDF_INIT-Struktur ..................................................................... 61212.8.3 UDF_ARGS-Struktur ................................................................... 61312.8.4 Rückgabewert ............................................................................ 61412.8.5 Benutzerdefinierte Funktionen erstellen ..................................... 61512.8.6 Benutzerdefinierte Funktion kompilieren, installieren
und ausführen ............................................................................ 61712.9 PostgreSQL – objektrelationales Datenbankverwaltungssystem ................. 619
12.9.1 PostgreSQL im Vergleich zu MySQL ........................................... 61912.9.2 Unterschiede in der Syntax zwischen MySQL und PostgreSQL ... 62012.9.3 PostgreSQL installieren .............................................................. 62112.9.4 Konfigurationsdateien bei PostgreSQL –
(postgresql.conf, pg_hba_conf) .................................................. 62212.9.5 CRASHKURS PostgreSQL ........................................................... 62412.9.6 PostgreSQL C-API – libpg ........................................................... 63112.9.7 Umgebungsvariablen und Passwortdatei .................................... 65112.9.8 PostgreSQL und Threads ............................................................ 65212.9.9 Ausblick ..................................................................................... 652
Inhalt
15
13 Terminal E/A und Benutzerschnittstellen für die Konsole .................. 653
13.1 termios ..................................................................................................... 65313.1.1 Terminalattribute bearbeiten ..................................................... 65413.1.2 Flags setzen und löschen ............................................................ 65913.1.3 Terminalidentifizierung .............................................................. 66513.1.4 Geschwindigkeitskontrolle – Baudrate von Terminals
einstellen ................................................................................... 66713.2 terminfo ................................................................................................... 671
13.2.1 terminfo verwenden .................................................................. 67213.2.2 terminfo initialisieren – setupterm() ........................................... 67313.2.3 Eigenschaften eines Terminals (Finden von capnames) –
tigetflag(), tigetnum() und tigetstr() ............................................ 67413.2.4 Mit terminfo-Eigenschaften arbeiten – putp(), tputs(), tparm() ... 677
13.3 ncurses – Halbgrafik .................................................................................. 67913.3.1 ncurses initialisieren ................................................................... 68013.3.2 Tastaturmodus und Ein- und Ausgabe ........................................ 68113.3.3 Eigenschaft der Fenster .............................................................. 69113.3.4 Scrolling .................................................................................... 69313.3.5 Attribute und Farben setzen ...................................................... 69613.3.6 Fensterroutinen ......................................................................... 70013.3.7 Mausprogrammierung mit ncurses ............................................. 706
14 GTK+ .................................................................................................... 715
14.1 Was ist GTK+? .......................................................................................... 71514.1.1 Was sind GDK und Glib? ............................................................ 71614.1.2 Schnittstellen von GTK+ zu anderen Programmiersprachen ........ 71714.1.3 GTK+ und GNOME .................................................................... 71714.1.4 GTK+ Version 1.2 und 2.x .......................................................... 71714.1.5 GTK+ – Aufbau des Kapitels ....................................................... 718
14.2 GTK+-Anwendungen übersetzen .............................................................. 71914.3 Eine Einführung in die Glib-Bibliothek ...................................................... 720
14.3.1 Datentypen ............................................................................... 72014.3.2 Routinen ................................................................................... 72114.3.3 Assertions-Funktionen ............................................................... 72314.3.4 Speicherverwaltung ................................................................... 72514.3.5 Stringbearbeitung ...................................................................... 72714.3.6 Selbstverwaltender Stringpuffer ................................................. 73114.3.7 Timer ......................................................................................... 73414.3.8 Dynamische Arrays .................................................................... 73614.3.9 Listen, Hashtabellen und binäre Bäume ..................................... 74014.3.10 Ausblick Glib ............................................................................. 740
Inhalt
16
14.4 Grundlagen der GTK+-Programmierung .................................................... 74114.4.1 Die Umgebung initialisieren ....................................................... 74114.4.2 Widgets erzeugen und ggf. die Attribute setzen ......................... 74214.4.3 Eine Callback-Funktion einrichten, um Events abzufangen ......... 74314.4.4 Eine GTK+-Anwendung beenden ............................................... 74514.4.5 Die hierarchische Anordnung der Widgets definieren ................. 74714.4.6 Widgets anzeigen ...................................................................... 74814.4.7 Signale und Events abfangen und bearbeiten –
(Events-)Verarbeitungsschleife ................................................... 74814.4.8 GTK+ und Umlaute (Zeichenkodierung) ..................................... 749
14.5 Fenster – GtkWindow ............................................................................... 75014.5.1 Dialogfenster (Dialogboxen) ....................................................... 75314.5.2 GtkMessageDialog ..................................................................... 757
14.6 Anzeige-Elemente .................................................................................... 75814.6.1 Text – GtkLabel .......................................................................... 76014.6.2 Trennlinie – GtkSeparator .......................................................... 76314.6.3 Grafiken – GtkImage .................................................................. 76414.6.4 Statusleiste – GtkStatusbar ......................................................... 76414.6.5 Fortschrittsbalken – GtkProgressBar ........................................... 765
14.7 Behälter .................................................................................................... 76514.7.1 Boxen – GtkBox ......................................................................... 76514.7.2 Aufteilungen, Register und Button-Box ...................................... 76714.7.3 Tabellen – GtkTable ................................................................... 77314.7.4 Ausrichtung – GtkAlignment ...................................................... 776
14.8 Buttons und Toogled-Buttons ................................................................... 77714.8.1 Buttons allgemein ...................................................................... 78314.8.2 Radio-Buttons (GtkRadioButton) ................................................ 78314.8.3 GtkRadioButton, GtkCheckButton und GtkToggleButton ........... 78414.8.4 Signale für Buttons (GtkButton) ................................................. 784
14.9 Dateneingabe ........................................................................................... 78514.9.1 Textfelder – GtkEntry ................................................................. 79114.9.2 Schieberegler – GtkScale ............................................................ 79214.9.3 Zahlenfelder – GtkSpinButton .................................................... 79314.9.4 Einstellungen – GtkAdjustment .................................................. 79414.9.5 GtkEditable ................................................................................ 795
14.10 Menü und Toolbar ................................................................................... 79514.10.1 Menü – GtkItemFactory ............................................................. 80014.10.2 Toolbar – GtkToolbar ................................................................. 80514.10.3 Options-Menü – GtkOptionsMenu ............................................ 80814.10.4 Combo-Boxen – GtkCombo ....................................................... 808
14.11 Mehrzeiliger Text ..................................................................................... 81214.11.1 Text(editor) – GtkTextView, GtkTextBuffer ................................. 82014.11.2 Scrollendes Fenster – GtkScrolledWindow ................................. 823
Inhalt
17
14.12 Auswählen (Selection) .............................................................................. 82414.12.1 Dateiauswahl – GtkFileSelection ................................................ 831
14.13 Events ...................................................................................................... 83314.14 Weitere Widget- und GTK+-Elemente im Überblick ................................. 838
14.14.1 Bäume und Listen ...................................................................... 83814.14.2 Lineale ....................................................................................... 83914.14.3 Zwischenablage (Clipboard) ....................................................... 83914.14.4 Drag and Drop ........................................................................... 83914.14.5 Stock Items – Repertoire-Einträge .............................................. 83914.14.6 Signale ....................................................................................... 83914.14.7 Ressource-Files .......................................................................... 83914.14.8 Widget-Entwicklung .................................................................. 83914.14.9 GDK .......................................................................................... 840
15 Übersicht über weitere beliebte GUI-Bibliotheken ............................ 841
15.1 gtkmm – GTK+ für C++ ............................................................................. 84115.1.1 Programmbeispiel ...................................................................... 84215.1.2 GUI-Designer GLADE für GTK+ und gtkmm ............................... 844
15.2 wxWidget ................................................................................................. 84515.2.1 Programmbeispiel ...................................................................... 84515.2.2 GUI-Designer wxFormBuilder .................................................... 847
15.3 FLTK ......................................................................................................... 84815.3.1 Programmbeispiel ...................................................................... 84815.3.2 GUI-Designer FLUID .................................................................. 849
15.4 Qt ............................................................................................................ 84915.4.1 Programmbeispiel ...................................................................... 85015.4.2 Der GUI-Designer von Qt .......................................................... 851
15.5 Die niedrige Ebene – X-Window-Programmierung .................................... 85215.6 Multimediabibliotheken ........................................................................... 852
15.6.1 SDL ........................................................................................... 85215.6.2 Allegro ...................................................................................... 85315.6.3 OpenGL (bzw. Mesa 3D) ........................................................... 85415.6.4 Programmbeispiel ...................................................................... 855
16 Werkzeuge für Programmierer ............................................................ 859
16.1 Der Compiler gcc ...................................................................................... 85916.1.1 Standardgebrauch des gcc ......................................................... 86116.1.2 Linken von Programmbibliotheken ............................................ 86116.1.3 Dateien, die GCC kennt ............................................................. 86216.1.4 Ausgabedateien bei jedem einzelnen Schritt der
Übersetzung erstellen ................................................................ 863
Inhalt
18
16.1.5 Noch mehr Optionen ................................................................. 86416.1.6 Optionen für Warnmeldungen ................................................... 86416.1.7 Präprozessor-Optionen .............................................................. 86516.1.8 Debuggen und Profiling ............................................................. 86516.1.9 Optimierungsflags ...................................................................... 865
16.2 Make ........................................................................................................ 86616.2.1 Erzeugen eines Makefiles ........................................................... 86816.2.2 Variablen, Makros und Abkürzungen ......................................... 87416.2.3 Implizite Regeln ......................................................................... 87616.2.4 Musterregeln ............................................................................. 87816.2.5 make zur Installation verwenden ................................................ 87816.2.6 make-Optionen ......................................................................... 87916.2.7 Ausblick ..................................................................................... 880
16.3 Bibliotheken erstellen ............................................................................... 88016.3.1 Statische Bibliotheken erstellen ................................................. 88016.3.2 Dynamische Bibliotheken (Shared Libraries) erstellen ................. 88316.3.3 Dynamisches Nachladen von Bibliotheken ................................. 886
16.4 RPM ......................................................................................................... 88816.4.1 Einführung in RPM .................................................................... 88916.4.2 Verzeichnisse, die RPM benötigt ................................................ 89016.4.3 Ein eigenes RPM-Paket erstellen ................................................ 89116.4.4 Sources ...................................................................................... 89116.4.5 Die Spec-Datei .......................................................................... 89216.4.6 Paket erstellen ........................................................................... 89516.4.7 Das Paket installieren ................................................................. 897
16.5 RCS und CVS ............................................................................................ 89816.5.1 Software-Configuration-Management-Systeme (SCM) ................ 89916.5.2 RCS ............................................................................................ 89916.5.3 CVS ........................................................................................... 908
16.6 Zeitmessung von Programmen .................................................................. 92316.6.1 Einfache Zeitmessung mit TIME – Laufzeit von Prozessen ........... 92316.6.2 Profiling mit GPROF – Laufzeit von Funktionen .......................... 92416.6.3 Analyse mit GCOV ..................................................................... 927
16.7 Debuggen mit gdb und ddd ..................................................................... 92916.8 STRACE – Systemaufrufe verfolgen ............................................................ 93916.9 Memory Leaks und unerlaubte Speicherzugriffe ........................................ 942
16.9.1 efence ........................................................................................ 94216.9.2 valgrind ..................................................................................... 945
16.10 Ausblick ................................................................................................... 948
Inhalt
19
17 Shellprogrammierung .......................................................................... 951
17.1 Was ist eine Shell und was kann sie? ........................................................ 95117.2 Was sind Shellskripts und wann ist ihr Einsatz sinnvoll? ............................ 95117.3 Wann brauche ich keine Shellskripts? ....................................................... 95317.4 Verschiedene Shelltypen ........................................................................... 953
17.4.1 Erweiterungen der Bourne-Shell (sh) .......................................... 95417.4.2 Erweiterung der C-Shell (csh) ..................................................... 95517.4.3 Welche Shell sollte man kennen? ............................................... 955
17.5 Shellskripts ausführen ............................................................................... 95517.5.1 Shellskript im Hintergrund ausführen ......................................... 95617.5.2 Die Bedeutung der Subshell ....................................................... 95617.5.3 Die ausführende Shell festlegen ................................................. 95817.5.4 Shellskript beenden ................................................................... 963
17.6 Vom Shellskript zum Prozess .................................................................... 96417.7 Datenstrom (Stream) umleiten .................................................................. 965
17.7.1 Standardausgabe umleiten ......................................................... 96517.7.2 Standardfehlerausgabe umleiten ................................................ 96617.7.3 Standardausgabe und Standardfehlerausgabe verknüpfen .......... 96717.7.4 Standardeingabe umleiten ......................................................... 96817.7.5 Pipes ......................................................................................... 96917.7.6 Standardausgabe in zwei Richtungen mit tee ............................. 97017.7.7 Zusammenfassung der verschiedenen Umlenkungen .................. 971
17.8 Ersatzmuster (Namen-Expansion) zur Suche verwenden ............................ 97217.8.1 Beliebige Zeichenfolge * (Asterisk) ............................................. 97217.8.2 Beliebiges Zeichen ? ................................................................... 97317.8.3 Zeichenbereiche einschränken ................................................... 97317.8.4 Brace Extension (nur Bash und Korn-Shell) ................................. 97417.8.5 Tilde-Expansion (nur Bash und Korn-Shell) ................................ 97517.8.6 Zusammenfassung zu den Ersatzmustern .................................... 975
17.9 Variablen .................................................................................................. 97617.9.1 Zahlen ....................................................................................... 97917.9.2 Zeichenketten ............................................................................ 98417.9.3 Arrays (nur Bash und Korn-Shell) ............................................... 98917.9.4 Variablen exportieren ................................................................ 99117.9.5 Die Umgebungsvariablen (Shellvariablen) .................................. 99417.9.6 Auto-Variablen der Shell ............................................................ 995
17.10 Quotings .................................................................................................. 99717.10.1 Single und Double Quotings ...................................................... 99717.10.2 Kommandosubstitution (Back Quotes) ....................................... 998
17.11 Kommandozeilenargumente ..................................................................... 99917.11.1 Kommandozeilenparameter $1 bis $9 ........................................ 100017.11.2 Variable Anzahl von Parametern auswerten ............................... 1001
Inhalt
20
17.11.3 Die Anzahl der Argumente überprüfen ....................................... 100117.11.4 Der Befehl shift .......................................................................... 100217.11.5 Argumente für die Kommandozeile setzen – set ......................... 100317.11.6 Kommandozeilenoptionen auswerten ........................................ 1004
17.12 Kontrollstrukturen .................................................................................... 100617.12.1 Die if-Anweisung ....................................................................... 100617.12.2 Die else-Alternative für die if-Verzweigung ................................ 101017.12.3 Mehrfache Alternative mit elif ................................................... 101017.12.4 Das Kommando test .................................................................. 101117.12.5 Dateistatus ermitteln ................................................................. 101517.12.6 Ausdrücke logisch verknüpfen .................................................... 101717.12.7 Verzweigungen mit case ............................................................ 102017.12.8 Schleifen .................................................................................... 1022
17.13 Terminal-Eingabe und -Ausgabe ............................................................... 102817.13.1 Ausgabe ..................................................................................... 102817.13.2 Eingabe ..................................................................................... 103117.13.3 Umlenkungen mit exec und File-Deskriptoren erzeugen ............. 104317.13.4 Named Pipes ............................................................................. 104617.13.5 Menü mit select (nur Bash und Korn-Shell) ................................ 1047
17.14 Funktionen ............................................................................................... 105017.14.1 Funktionsaufruf .......................................................................... 105017.14.2 Externe Funktionen verwenden .................................................. 105017.14.3 Parameterübergabe .................................................................... 105117.14.4 Rückgabewert aus einer Funktion .............................................. 1052
17.15 Signale ..................................................................................................... 105417.15.1 Signale senden ........................................................................... 105417.15.2 Signale in einem Shellskript abfangen – trap .............................. 105517.15.3 Signal-Handler einrichten ........................................................... 105617.15.4 Signale ignorieren oder zurücksetzen ......................................... 1057
17.16 Prozess- und Skriptausführung .................................................................. 105817.16.1 Auf Prozesse warten .................................................................. 105817.16.2 Hintergrundprozess hervorholen ................................................ 105817.16.3 Jobverwaltung ........................................................................... 105917.16.4 Explizite Subshell verwenden ..................................................... 106017.16.5 Kommunikation zwischen Shellskripten ..................................... 106117.16.6 Skripte zeitgesteuert ausführen .................................................. 1065
17.17 Ein Shellskript bei der Ausführung ............................................................ 1065
Anhang ....................................................................................................... 1067
A Sicherheit unter Linux .......................................................................................... 1067A.1 Viren und Trojaner ................................................................................... 1067A.2 Der Superuser (su) .................................................................................... 1068
Inhalt
21
A.3 Überlaufen von Logfiles ............................................................................ 1068A.4 Zugriffsrechte auf Dateien ........................................................................ 1069A.5 Das SUID-Bit ............................................................................................ 1069A.6 Programme ohne Ausführrechte ............................................................... 1071A.7 Buffer Overflow (Pufferüberlauf) ............................................................... 1071A.8 Race Condition ......................................................................................... 1073A.9 Temporäre Dateien ................................................................................... 1074A.10 chroot ...................................................................................................... 1075A.11 Umgebungsvariablen ................................................................................ 1076A.12 Zugriffsrechte – häufig gemachte Fehler .................................................... 1079A.13 system() und popen() ................................................................................ 1079A.14 Offene Filedeskriptoren ............................................................................ 1080A.15 Core Dump .............................................................................................. 1082A.16 SQL Injection ........................................................................................... 1083A.17 Filedeskriptor-Überlauf mit select() ........................................................... 1085
B Funktionsreferenz ................................................................................................ 1087B.1 ANSI C ..................................................................................................... 1087B.2 ANSI C99 ................................................................................................. 1105B.3 Elementare E/A-Funktionen ...................................................................... 1112B.4 Fortgeschrittene E/A-Funktionen .............................................................. 1114B.5 Verzeichnisse ............................................................................................ 1120B.6 Attribute von Dateien und Verzeichnissen ................................................ 1122B.7 Links ........................................................................................................ 1124B.8 Prozess und Prozessverwaltungsfunktionen .............................................. 1125B.9 Signale – Das neue Signalkonzept ............................................................. 1126B.10 Interprozesskommunikationen .................................................................. 1129B.11 Sys-V-Interprozesskommnunikationen ...................................................... 1130B.12 Threadprogrammierung ............................................................................ 1132B.13 Netzwerkprogrammierung ........................................................................ 1135B.14 MySQL C-API ........................................................................................... 1141B.15 PostgreSQL C-API ..................................................................................... 1146B.16 Weitere Funktionsreferenzen auf der Buch-CD ......................................... 1149
C Linux-Unix-Kommandoreferenz ............................................................................ 1151C.1 Dateiorientierte Kommandos .................................................................... 1151C.2 Verzeichnisorientierte Kommandos .......................................................... 1168C.3 Verwaltung von Benutzern und Gruppe .................................................... 1169C.4 Programm- und Prozessverwaltung ........................................................... 1173C.5 Speicherplatzinformationen ...................................................................... 1179C.6 Dateisystem-Kommandos ......................................................................... 1180C.7 Archivierung und Backup .......................................................................... 1190C.8 Systeminformationen ................................................................................ 1202C.9 System-Kommandos ................................................................................. 1203
Inhalt
22
C.10 Druckeradministration .............................................................................. 1205C.11 Netzwerkbefehle ...................................................................................... 1205C.12 Benutzerkommunikation .......................................................................... 1216C.13 Bildschirm- und Terminalkommandos ....................................................... 1217C.14 Online-Hilfen ........................................................................................... 1219C.15 Alles rund um PostScript-Kommandos ...................................................... 1221C.16 Gemischte Kommandos ............................................................................ 1221
D Inhalt der Buch-CD .............................................................................................. 1223
Index ........................................................................................................................... 1225
129
Das /proc-(Pseudo-)Filesystem beinhaltet eine Menge Informationen zu Ihrem System. Ob Sie nun Informationen zum aktuellen Prozess, zur Hardware in Ihrem System oder zum Kernel benötigen, all dies finden Sie im /proc-Filesystem.
4 Zugriff auf Systeminformationen
Da der Kernel den kompletten Rechner verwaltet, besitzt dieser auch eine Menge Informatio-nen zum System, auf dem dieser läuft. Diese Informationen werden vom Kernel in Pseudo-Dateien und Pseudo-Verzeichnissen angelegt. All diese Dateien liegen im /proc-Verzeichnis.Auf die Einträge im /proc-Verzeichnis können Sie wie auf gewöhnliche Dateien zugreifen;meistens allerdings nur lesend. Dieses (Pseudo-)Dateisystem belegt keinerlei Plattenplatz undbefindet sich im Hauptspeicher (RAM). Sie können sich gerne einen Überblick zum /proc-Verzeichnis verschaffen.
$ ls -l /proc | less
Auf alle diese Informationen können Sie (fast) ohne weiteres lesend zugreifen. Wollen Sie z. B.den aktuellen Kernel Ihres Systems ermitteln und ausgeben lassen, können Sie auf die Datei/proc/version zugreifen:
$ cat /proc/versionLinux version 2.6.27-7-generic (buildd@palmer)(gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu11))#1 SPM Tue Jan 4 19:33:20 UTC 2009
Schon erhalten Sie die aktuelle Kernel-Version, das Datum, an dem dieser kompiliert wurde,und die Version des zugehörigen Compilers als String zurück.
4.1 Informationen aus dem /proc-Verzeichnis herausziehen
Als Programmierer dürfte Sie in erster Linie interessieren, wie Sie mit Ihrem Programm andiese Informationen kommen. Als Beispiel dient hier die Speicherauslastung des Systems.Hierbei soll lediglich ermittelt werden, wie viel RAM insgesamt auf Ihrem System zur Verfü-gung steht. Diese Informationen erhalten Sie aus /proc/meminfo:
$ cat /proc/meminfoMemTotal: 514296 kBMemFree: 10428 kBBuffers: 122940 kBCached: 120112 kBSwapCached: 1152 kBActive: 337336 kB
Zugriff auf Systeminformationen
130
4
Inactive: 122512 kBHighTotal: 0 kBHighFree: 0 kBLowTotal: 514296 kBLowFree: 10428 kBSwapTotal: 409616 kBSwapFree: 385128 kB...
Uns interessiert hier der Wert »MemTotal«. Der einfachste Weg, diesen Wert auszulesen, istes, /proc/meminfo mit fopen() zu öffnen, in einen Puffer einzulesen und nach der Stringfolge»MemTotal« zu suchen. Anschließend kann der Wert mit der sscanf() aus dem Puffer in einedafür vorgesehene Variable übergeben werden. Hier das mögliche Beispiel:
/* memory.c */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>
static long get_mem_total (void) {FILE *fp;char buffer[1024];size_t bytes_read;char *match;long mem_tot;
if((fp = fopen("/proc/meminfo", "r")) == NULL) {perror("fopen()");exit(EXIT_FAILURE);
}
bytes_read = fread (buffer, 1, sizeof (buffer), fp);fclose (fp);if (bytes_read == 0 || bytes_read == sizeof (buffer))
return 0;buffer[bytes_read] = '\0';/* Suchen nach der Stringfolge "Memtotal" */match = strstr (buffer, "MemTotal");if (match == NULL) /* Nicht gefunden */
return 0;sscanf (match, "MemTotal: %ld", &mem_tot);return (mem_tot/1024); /* 1MB = 1024KB */
}
int main (void) {long memory = get_mem_total();if(memory == 0)
printf("Konnte RAM nicht ermitteln\n");else
Hardware-/Systeminformationen ermitteln 4.2
131
printf("Vorhandener Arbeitsspeicher: %ldMB\n" ,memory);return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o memory memory.c$ ./memoryVorhandener Arbeitsspeicher: 249MB
So oder so ähnlich können Sie in der Regel bei allen Informationen vorgehen, die Sie im/proc-Verzeichnis finden und benötigen. Sie sollten allerdings darauf achten, dass sich derName des Eintrags im /proc-Verzeichnis bei neueren Kernel-Versionen ändern könnte. Sokönnte vielleicht in absehbarer Zeit aus /proc/meminfo /proc/more_meminfo (nur ein Bei-spiel) werden. Bedenken Sie dies, wenn Sie Ihr Programm auf dem Stand der Zeit halten wol-len.
4.2 Hardware-/Systeminformationen ermitteln
Viele Einträge im /proc-Filesystem erlauben Ihnen, Informationen zur Hardware des Systemszu ermitteln. Das sind interessante Informationen für Systemadministratoren und auch fürden Programmierer von Programmen. Hier einige häufig verwendete Einträge im Überblick.
4.2.1 CPU-Informationen – /proc/cpuinfo
/proc/cpuinfo beinhaltet Informationen zur CPU (Prozessor) oder zu den CPUs, die aufIhrem System laufen. Ausgegeben wird dabei die Anzahl der Prozessoren. Ist der Wert von»processor« wie im Beispiel 0, so kann dies bedeuten, dass es sich um ein Single-Prozessor-System handelt.
Weitere Daten finden Sie zu der CPU-Familie, dem Modell, der Frequenz, der Revision undnoch einigem mehr. Hierzu ein Ausschnitt aus meinem System:
$ cat /proc/cpuinfoprocessor : 0vendor_id : GenuineIntel
Hinweis
Bei diesem Buch handelt es sich nicht um ein Buch zur Erklärung von Hardware. Sollten Sie keinerleiKenntnisse vom Innenleben Ihres PC haben, dann wäre zusätzliche Literatur recht sinnvoll.
Hinweis
Auf SMP (Multi-CPU-Maschinen) ist der Wert von processor ... nicht direkt 0, sondern es wird fürjeden Prozessor einzeln »processor, vendor_id« etc. angezeigt, weshalb »auf 0 gesetzt« nicht ganzkorrekt ist.
Zugriff auf Systeminformationen
132
4
cpu family : 15model : 2model name : Intel(R) Pentium(R) 4 CPU 1.60GHzstepping : 4cpu MHz : 1594.865cache size : 512 KBfdiv_bug : nohlt_bug : nof00f_bug : nocoma_bug : nofpu : yesfpu_exception : yescpuid level : 2wp : yesflags : fpu vme de pse tsc msr pae mce cx8 sep mtrr pge mca cmov pat
pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tmbogomips : 3185.04
Für mehr Informationen zur CPU sei die Dokumentation zu cpuid des Intel Architecture Soft-ware Developers Manual, Volume 2, empfohlen.
4.2.2 Geräteinformationen – /proc/devices
Hier sind Informationen zu allen verfügbaren Geräten (zeichen- und blockorientiert) wie derFestplatte, dem Diskettenlaufwerk, der seriellen und parallelen Schnittstelle usw. aufgelistet.
4.2.3 Speicherauslastung – /proc/meminfo
(S. o. unter 4.1.) In dieser Pseudo-Datei kann der aktuelle Speicherstatus ausgelesen werden.Angezeigt werden der vorhandene und der belegte Speicher (sowohl der physikalische alsauch der Swap-Speicher). Ebenfalls lässt sich ermitteln, wie viel davon für geteilten Speicher(Shared Memory), Puffer und Caches belegt ist, die der Kernel benutzt.
4.2.4 Weitere Hardware-Informationen zusammengefasst
Es gibt natürlich noch eine Menge mehr Informationen zur Hardware im /proc-Verzeichnis,die allerdings zum Teil auch von der Konfiguration des Systems abhängt. Hierzu eine kleineZusammenfassung zu weiteren gängigen Einträgen im /proc-Verzeichnis.
Verzeichniseintrag Bedeutung
/proc/dma Liste von belegten DMA-Kanälen, die für einen Treiber reserviert sind, und der Name des Treibers
/proc/interrupts Liste der benutzten Interrupts mit Anzahl der passierten Interrupts seit System-start, Typ des Interrupts und Angabe, durch welche Module dieser Interrupt ver-wendet wird
Tabelle 4.1 Systeminformationen im /proc-Verzeichnis
Hardware-/Systeminformationen ermitteln 4.2
133
Das folgende Listing soll Ihnen jetzt demonstrieren, wie einfach es ist, aus dem System nütz-liche Informationen zur Hardware ausgeben zu lassen. Das Beispiel stellt eine einfacheSchnittstelle dar, die es zu erweitern gilt. Damit haben Sie schon die Grundlage für ein Sys-temprogramm zur Hand. Sollte es ein Konsolenprogramm werden, dann müssten Sie die Aus-gabe benutzerfreundlich anpassen; ebenfalls dann, wenn Sie nur einzelne Informationen aus-geben wollen. Natürlich können Sie über das Programm auch ein grafisches Frontend ziehen.Denn auch die Systemprogramme von KDE oder GNOME machen nichts anderes, als dieDaten aus dem /proc-Filesystem zu lesen und entsprechend auszugeben.
/* myinfo.c */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define BUF 4096 /* Anpassen */
enum { CPU, DEV, DMA, INT, IOP, MEM, VERS, SCSI, EXIT };
static const char *info[] = {"/proc/cpuinfo", "/proc/devices", "/proc/dma","/proc/interrupts", "/proc/ioports", "/proc/meminfo","/proc/version", "/proc/scsi/scsi"
};
static void get_info (const int inf) {FILE *fp;
/proc/ioports Eine Liste aller belegten IO-Ports für die Ein-/Ausgabe wie die Festplatte, Ether-net, Soundkarte, Modem, USB etc. (gemappte Hardware-Speicherbereiche fin-den sich in /proc/iomem).
/proc/stat Allgemeine Informationen über den Status der Prozessoren. Diese Informationen werden vom Programm procinfo verwendet und übersichtlich aufgelistet.
/proc/uptime Anzahl der Sekunden, seitdem das System gestartet ist, und wie lange davon die CPU mit dem Nichtstun (HLT) (Leerlaufzeit = engl. idle) verbracht hat
/proc/scsi/ Unterverzeichnis mit Informationen zu SCSI-Geräten
/proc/scsi/scsi Sofern Sie eine SCSI-Schnittstelle besitzten, finden Sie hier eine Auflistung aller Geräte.
/proc/ide/ Unterverzeichnis mit Informationen zu IDE-Geräten
/proc/apm Informationen zum Advanced Power Management (z. B. ein Text wie »AC off-line« bedeutet, dass ein Notebook im Batteriebetrieb läuft)
/proc/mounts Liste aller »gemounteten« Dateisysteme
/proc/net/ Unterverzeichnis zu Netzwerkinformationen
/proc/loadavg Duchschnittliche Systemauslastung (eine Minute, drei Minuten, fünf Minuten, aktive Prozesse/Anzahl Prozesse, und zuletzt benutzte PID)
Verzeichniseintrag Bedeutung
Tabelle 4.1 Systeminformationen im /proc-Verzeichnis (Forts.)
Zugriff auf Systeminformationen
134
4
char buffer[BUF];size_t bytes_read;
if((fp = fopen(info[inf], "r")) == NULL) {perror("fopen()");return;
}bytes_read = fread (buffer, 1, sizeof (buffer), fp);fclose (fp);if (bytes_read == 0 || bytes_read == sizeof (buffer))
return;buffer[bytes_read] = '\0';
printf("%s",buffer);printf("Weiter mit ENTER");getchar();return;
}
int main (void) {int auswahl;do {
printf("Wozu benötigen Sie Informationen?\n\n");printf("-%d- Prozessor\n", CPU);printf("-%d- Geräte\n", DEV);printf("-%d- DMA\n", DMA);printf("-%d- Interrupts\n", INT);printf("-%d- I/O-Ports\n", IOP);printf("-%d- Speicher\n", MEM);printf("-%d- Version\n", VERS);printf("-%d- SCSI\n", SCSI);printf("-%d- Programmende\n", EXIT);printf("\nIhre Auswahl : ");do{ scanf("%d",&auswahl); } while(getchar() != '\n');
switch(auswahl) {case CPU : get_info(CPU); break;case DEV : get_info(DEV); break;case DMA : get_info(DMA); break;case INT : get_info(INT); break;case IOP : get_info(IOP); break;case MEM : get_info(MEM); break;case VERS: get_info(VERS); break;case SCSI: get_info(SCSI); break;case EXIT: printf("Bye\n"); break;default : printf("Falsche Eingabe?\n");
}} while(auswahl != EXIT);return EXIT_SUCCESS;
}
Prozessinformationen 4.3
135
Das Programm im Einsatz:
$ ./myinfoWozu benötigen Sie Informationen?
-0- Prozessor-1- Geräte-2- DMA-3- Interrupts-4- I/O-Ports-5- Speicher-6- Version-7- SCSI-8- Programmende
Ihre Auswahl : 7Attached devices:Host: scsi0 Channel: 00 Id: 00 Lun: 00Vendor: VMware, Model: VMware Virtual S Rev: 1.0Type: Direct-Access ANSI SCSI revision: 02
Host: scsi2 Channel: 00 Id: 00 Lun: 00Vendor: MATSHITA Model: DVD-RAM UJ-850S Rev: 1.60Type: CD-ROM ANSI SCSI revision: 05
Weiter mit ENTER
4.3 Prozessinformationen
Neben Hardware-Informationen können Sie aus dem /proc-Filesystem auch Informationen zuden einzelnen Prozessen ermitteln. Auch Programme wie ps oder top nutzen diese Möglich-keit, um Ihnen Informationen zu einem Prozess zu liefern. Den Prozessen und deren Verwal-tung ist extra noch ein Kapitel gewidmet. Das /proc-Filesystem beinhaltet für jeden Prozessextra ein Unterverzeichnis. Der Name des Verzeichnisses entspricht der Prozess-ID. Lassen Siesich am besten das /proc-Verzeichnis ausgeben:
$ ls -l /proc | lessdr-xr-xr-x 3 root root 0 2003-11-13 23:46 1dr-xr-xr-x 3 root root 0 2003-11-13 23:46 12dr-xr-xr-x 3 root root 0 2003-11-13 23:46 1201dr-xr-xr-x 3 root root 0 2003-11-13 23:46 1278dr-xr-xr-x 3 bin root 0 2003-11-13 23:46 1303...dr-xr-xr-x 4 root root 0 2003-11-13 23:47 tty-r--r--r-- 1 root root 0 2003-11-13 23:47 uptime
Hinweis
Fragen zum Löschen des Bildschirms in einem Terminal werden in Kapitel 13, »Terminal E/A undBenutzerschnittstellen für die Konsole«, beantwortet.
Zugriff auf Systeminformationen
136
4
-r--r--r-- 1 root root 0 2003-11-13 23:47 versiondr-xr-xr-x 3 root root 0 2003-11-13 23:47 video
Diese Verzeichnisse werden als Prozessverzeichnisse bezeichnet, da sich diese auf die Prozess-ID (PID) beziehen (komischerweise taucht es in den 2.6er-Sources als »TGID« auf ...) und Infor-mationen zu diesem Prozess beinhalten. Eigentümer und Gruppe eines jeden solchen PID-Verzeichnisses werden auf die ID des Benutzers, der den Prozess ausführt, gesetzt. (Eine nach-trägliche Änderung über setuid() ändert nur den Eigentümer des Verzeichnisses, nicht dender Dateien.) Nach Beendigung des Prozesses wird der Eintrag im /proc-Verzeichnis ebenfallswieder gelöscht. Während der Ausführung eines Prozesses können Sie aus dem Verzeichnisdie nun folgenden nützlichen Informationen dazu erhalten:
#include <unistd.h.>int main(void) { setuid(25); while(1); }
Im Hintergrund ausführen (»./a.out &«) und reingucken:
# ./a.out &[1] 2275# ls -l /proc/2275/insgesamt 0-r--r--r-- 1 tot users 0 2004-08-14 02:36 cmdline-r-------- 1 tot users 0 2004-08-14 02:36 environlrwxrwxrwx 1 tot users 0 2004-08-14 02:36 exe->./a.outdr-x------ 2 tot users 0 2004-08-14 02:36fd-rw------ 1 tot users 0 2004-08-14 02:36 mapped_base-r--r--r-- 1 tot users 0 2004-08-14 02:36 maps-rw------- 1 tot users 0 2004-08-14 02:36 mem-r--r--r-- 1 tot users 0 2004-08-14 02:36 mountslrwxrwxrwx 1 tot users 0 2004-08-14 02:36 root -> /
-r--r--r-- 1 tot users 0 2004-08-14 02:36 stat-r--r--r-- 1 tot users 0 2004-08-14 02:36 statm-r--r--r-- 1 tot users 0 2004-08-14 02:36 status
Gilt auch für seteuid() und setreuid().
4.3.1 /proc/$pid/cmdline
Sie finden z. B. ein Verzeichnis mit der ID 2167 und wollen wissen, was in der Kommando-zeile des Prozesses steht, dann müssen Sie nur cmdline abfragen:
$ cat /proc/2167/cmdlineanjuta
Der Prozess mit der ID 2167 ist also das Programm (die Entwicklungsumgebung) Anjuta beider Ausführung. Wollen Sie alle Prozesse ausgeben lassen, können Sie dies auch folgenderma-ßen machen:
# cat /proc/[0-9]*/cmdline
Da die Ausgabe allerdings zu wünschen übrig lässt, sollten Sie hierfür das Kommando stringseinsetzen:
Prozessinformationen 4.3
137
# strings -f /proc/[0-9]*/cmdline
Und schon bekommen Sie alle zu den Prozess-IDs gehörenden Namen ausgegeben. Mit cmd-line können Sie eine einzelne Zeile des Prozesses ausgeben, worin der Name (oder auch dasKommando) des Programms mit allen Argumenten enthalten ist.
Falls strings bei Ihnen nicht machen will, was es soll, können Sie auch folgende Eingabe ver-wenden:
# grep -Ha "" /proc/[0-9]*/cmdline | tr '\0' " "
4.3.2 /proc/$pid/environ
Jeder Prozess hat außerdem auch eine Prozessumgebung. Welche Umgebungsvariablen fürwelchen Prozess gesetzt sind, können Sie mit environ im /proc-Verzeichnis der Prozess-IDerfragen. Hier soll weiterhin der Prozess mit der ID 2167, die EntwicklungsumgebungAnjuta, beobachtet werden:
$ strings -f /proc/2167/environ/proc/2167/environ: LESSKEY=/etc/lesskey.bin/proc/2167/environ:MANPATH=/usr/local/man:/usr/share/man:/../proc/2167/environ:INFODIR=/usr/local/info:/usr/share/.../proc/2167/environ: NNTPSERVER=news.../proc/2167/environ: KDE_FULL_SESSION=true/proc/2167/environ: KDE_MULTIHEAD=false/proc/2167/environ: SESSION_MANAGER=local/linux:/tmp/.IC.../proc/2167/environ: KDE_STARTUP_ENV=linux;1068759934;528706;1890
Wenn es auch hier wieder nicht mit strings klappen sollte, dann sollten Sie es mit Folgendemprobieren:
# tr '\0' '\n' < /proc/2167/environ
4.3.3 /proc/self
Wollen Sie die aktuelle Prozess-ID Ihres eigenen Programms ermitteln, so können Sie /proc/self auswerten. Bei diesem Eintrag handelt es sich um einen symbolischen Link des aktuelllaufenden Prozesses. Das aktuelle Programm sollten Sie aber nicht mit der Shell verwechseln(versuchen Sie selbst: ls -dl /proc/self und ls -dl /proc/$$). Diesen symbolischen Linkkönnen Sie mit der Funktion readlink() auslesen. Hier ein Listing, das demonstriert, wie Siedie Prozess-ID des aktuell laufenden Programms ermitteln können:
/* my_getpid.c */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>
int main (void) {
Zugriff auf Systeminformationen
138
4
char buf[64];int pid;
memset(buf, '\0', sizeof(buf));readlink("/proc/self", buf, sizeof(buf) - 1);sscanf(buf, "%d", &pid);printf("Meine Prozess-ID lautet: %d\n", pid);return EXIT_SUCCESS;
}
Das Programm bei seiner Ausführung:
$ gcc -o my_getpid my_getpid.c$ ./my_getpidMeine Prozess-ID lautet: 2256
4.3.4 /proc/$pid/fd/
Ein weiterer Eintrag ist fd, ein Unterverzeichnis, das alle Einträge von Filedeskiptoren bein-haltet, die ein Prozess geöffnet hat. Jeder Eintrag darin ist ein symbolischer Link, der auf einebestimmte Datei lesend oder schreibend zurückgreift. Natürlich sind hierbei auch immer dieStandard-Filedeskriptoren (0, 1, 2) enthalten (es sei denn, sie wurden geschlossen, was vieleDämonen etc. tun!). Öffnen Sie eine Konsole, und geben Sie ps ein:
$ psPID TTY TIME CMD1988 pts/1 00:00:00 bash2827 pts/1 00:00:00 ps
Wollen Sie jetzt wissen, welche Filedeskiptoren bash offen hat, machen Sie Folgendes:
$ ls -l /proc/1988/fd/insgesamt 0lrwx------ 1 tot users 64 2003-11-14 00:50 0->/dev/pts/1lrwx------ 1 tot users 64 2003-11-14 00:50 1->/dev/pts/1lrwx------ 1 tot users 64 2003-11-14 00:50 2->/dev/pts/1
Hierbei können Sie die drei Standard-Filedeskriptoren als symbolischen Link auf dem Pseudo-Terminal pts/1 wieder erkennen. Sie können jetzt ohne weiteres auf diese Filedeskriptorenzugreifen:
$ echo "Ein Beweis gefällig?" > /proc/1988/fd/1Ein Beweis gefällig?
Hier wurde die Standardausgabe von echo auf die eigentliche Standardausgabe des Pseudo-Terminals »geleitet«.
Hinweis
Das Beispiel stellt natürlich nur eine Demonstration dar. In der Praxis werden Sie auf die Funktiongetpid() für die Ermittlung der Prozess-ID des laufenden Programms zurückgreifen.
Kernel-Informationen 4.4
139
4.3.5 /proc/$pid/statm
Informationen zur Speicherbelegung eines Prozesses werden im statm-File hinterlegt. WollenSie z. B. die Speicherbelegung der Entwicklungsumgebung Anjuta ermitteln, gehen Sie fol-gendermaßen vor:
$ cat /proc/2167/statm2671 2670 1678 302 0 2368 992
Sie bekommen dabei sieben verschiedene Zahlen zurück. Hier die Bedeutung der einzelnenZahlen von links nach rechts:
� Gesamte Programmgröße in Kilobyte
� Größe von Speicherteilen in Kilobyte
� Anzahl von »geshareten« Seiten (so genannte Pages)
� Anzahl Seiten von Programmcode
� Anzahl Seiten von Stack/Daten
� Anzahl Seiten von Bibliotheksprogrammcode
� Anzahl von unsauberen Seiten
Es gibt noch weitere nützliche Informationen zu den Prozessen, die Sie jetzt in der folgendenTabellen aufgelistet finden. $pid ersetzen Sie bitte für eine echte Prozess-ID.
4.4 Kernel-Informationen
Viele Einträge im /proc-Verzeichnis geben auch Informationen zum laufenden Kernel aus.Der Ort der Kernel-Informationen ist unterschiedlich, viele liegen im /proc-Verzeichnis selbstund weitere in den Unterverzeichnissen wie /proc/sys oder /proc/sys/kernel.
Verzeichniseintrag Bedeutung
/proc/$pid/status Darin finden Sie die formatierten Informationen, die Sie ebenfalls mit /proc/$pid/stat (allerdings nicht formatiert) ermitteln können. Dies sind Informatio-nen wie die Prozess-ID, die reale und effektive User- und Group-ID, Speicher-verbrauch, Bitmasken usw.
/proc/$pid/stat Siehe /proc/$pid/status.Dies ist eine etwas kompaktere Datei, die sich besser als »status« mit sscanf() oder fscanf() verwenden lässt.
/proc/$pid/cwd Ein symbolischer Link zum aktuellen Arbeitsverzeichnis des Prozesses
/proc/$pid/exe Ein Verweis auf die ausführbare Programmdatei
/proc/$pid/root Ein Link zum Root-Verzeichnis, das als Wurzelverzeichnis für den Prozess gilt (siehe chroot())
/proc/$pid/maps Beinhaltet Speicher-Mappings zu den verschiedenen laufenden Dateien und Bibliotheken, die mit diesem Prozess zusammenhängen. Die Datei kann sehr lang werden, wenn ein umfangreicher Prozess ausgeführt wird.
Tabelle 4.2 Prozessinformationen in den individuellen PID-Verzeichnissen
Zugriff auf Systeminformationen
140
4
Hierzu einige häufig benötigte Informationen, die in viele Anwendungen integriert sind:
$ cat /proc/sys/kernel/ostypeLinux$ cat /proc/sys/kernel/osrelease2.6.27-7-generic$ cat /proc/sys/kernel/version#1 SMP Tue Jan 4 19:33:20 UTC 2009$ cat /proc/sys/kernel/ctrl-alt-del0
Zuerst wurde abgefragt, was für ein Betriebssystem genau hier läuft (ostype), dann die Ver-sion des Kernels (osrelease) und wann dieser Kernel kompiliert wurde (version). Bei derletzten Abfrage wird überprüft, ob ctrl-alt-del gesetzt ist. Damit können Sie beeinflussen,ob init bei der Tastenkombination (Ctrl)+(Alt)+(Del) eine Aktion ausführen soll. Steht»ctrl-alt-del« auf 1, wird bei Tastendruck sofort ein BIOS-Reboot angeordnet. Steht »ctrl-alt-del« allerdings auf 0, wird init dazu angehalten, das System ordentlich herunterzufahrenWollen Sie diesen Parameter verändern, müssen Sie sich schnell als Superuser darstellen:
$ suPassword:********# echo 1 >> /proc/sys/kernel/ctrl-alt-del# exitexit$ cat /proc/sys/kernel/ctrl-alt-del1
Ein weiteres Beispiel. Sie haben eine CD-ROM eingelegt und gemountet. Nachdem Sie dieDaten von der CD-ROM gelesen haben, werfen Sie die CD-ROM ordnungsgemäß mit umountwieder aus und fahren anschließend das System herunter. Jetzt benötigen Sie aber die CD-ROM für einen Bekannten. Somit müssen Sie den PC leider nochmals anschalten. Wäre dochnett, wenn beim Ausgeben der CD-ROM die CD ausgeworfen wird. Für solche Fälle ist derEintrag in /proc/sys/dev/cdrom/autoeject zuständig. Der Wert ist mit der Voreinstellung 0versehen. Folgendermaßen können Sie dabei vorgehen, um den Wert auf 1 zu setzen:
$ mount /media/cdrom$ cat /proc/sys/dev/cdrom/autoeject0$ suPassword:********# echo 1 >> /proc/sys/dev/cdrom/autoeject# exitexit$ cat /proc/sys/dev/cdrom/autoeject1$ umount /media/cdrom
Beim Aushängen des CD-ROM-Laufwerkes müsste jetzt die CD ausgeworfen werden. Diesesnette Feature zeigt allerdings schon recht früh seine Schattenseiten, z. B. wenn die Hard-wareerkennung oder das Installationsprogramm das Device mehrmals öffnet und schließt.
Kernel-Informationen 4.4
141
Besonders fies auf engem Raum, wo Ihr CD-Laufwerk leiden wird, wenn es sich nicht vollstän-dig öffnen kann.
Sie hacken übrigens damit nicht im System herum. Viele dieser Kernel-Parameter wurdenbewusst so implementiert, damit Sie den Bedürfnissen der Anwender angepasst werden kön-nen, um ein höchstes Maß an Flexibilität zu erreichen. Da dabei in der Regel Superuser-Rechtebenötigt werden, sind diese Einstellungen meistens für den Systemadministrator vorgesehen.Daher macht es relativ wenig Sinn, wenn Sie in Ihrer Anwendung, die Sie schreiben, versu-chen, die Kernel-Parameter zu verändern. Dies würde bedeuten, dass Ihre Anwendung imSuperuser-Modus laufen müsste. Bedenken Sie, dass dies nicht immer möglich ist – und auchnicht sein sollte.
Für administrative Zwecke soll hierfür jedoch eine solche Anwendung erstellt werden. Aberwie schon erwähnt, Sie benötigen Superuser- Rechte (bzw. root-Rechte) für dieses Programm,sollten Sie etwas verändern wollen. Versucht der normale Anwender, etwas zu verändern,bekommt er ein »Permission denied« zurückgegeben. Hier das Beispiel mit anschließenderErläuterung.
/* kernelinf.c */#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h.>#include <string.h>#define BUF 4096
enum { EJECT, FILE_MAX, SHARED_MAX };enum { CDINFO, OS, RELEASE, VERSION };
static const char *sys[] = {/* Bei umount CD auswerfen */"/proc/sys/dev/cdrom/autoeject",/* max. Anzahl geöffneter Dateien pro Prozess */"/proc/sys/fs/file-max",/*max. Größe geteilter Speicher (Shared Memory) */"/proc/sys/kernel/shmmax"
};
static const char *info[] = {"/proc/sys/dev/cdrom/info", /* Infos zur CD-ROM */"/proc/sys/kernel/ostype", /* Welches Betriebssystem */"/proc/sys/kernel/osrelease" /* Kernel-Version */"/proc/sys/kernel/version" /* Kernel von wann */
};
static char *get_info (const char *inf) {FILE *fp;static char buffer[BUF];
Zugriff auf Systeminformationen
142
4
size_t bytes_read;
fp = fopen (inf, "r");if (fp == NULL) {perror("fopen()");return NULL; /* Fehler beim Öffnen */
}bytes_read = fread (buffer, 1, sizeof (buffer), fp);fclose (fp);if (bytes_read == 0 || bytes_read == sizeof (buffer))return NULL;
buffer[bytes_read] = '\0';return buffer;
}
static void set_sys (const char *sys, unsigned long set) {FILE *fp;char buf[32];
fp = fopen (sys, "w");if (fp == NULL) {perror ("fopen()");printf ("Weiter mit ENTER\n");getchar ();return;
}sprintf(buf, "%ld", set);fprintf (fp, "%s", buf);fclose (fp);return;
}
int main (int argc, char **argv) {int auswahl;unsigned int file_max;unsigned long shared_max;
do {printf ("Aktueller Zustand\n");printf ("Betriebssystem : %s", get_info (info[OS]));printf ("Kernel-Version : %s",
get_info (info[RELEASE]));printf ("Datum : %s",
get_info (info[VERSION]));printf ("------------------------------------\n");printf ("Verändern können Sie Folgendes ...\n");printf ("-0- Bei \"umount\" CD auswerfen"
" Aktuell:%s", get_info (sys[EJECT]));printf ("-1- Max. Anzahl geöffneter Dateien pro"
Kernel-Informationen 4.4
143
" Prozess Aktuell:%s",get_info (sys[FILE_MAX]));
printf ("-2- Max. Größe des geteilten Speichers"" (KB) Aktuell %s",
get_info (sys[SHARED_MAX]));printf ("------------------------------------\n");printf ("Informationen bekommen Sie zu ...\n");printf ("-3- CD-ROM\n");printf ("------------------------------------\n");printf ("-4- ENDE\n");printf ("Ihre Auswahl bitte (0-4) : ");
do { scanf ("%d", &auswahl); } while (getchar ()!='\n');switch (auswahl) {case EJECT:
if (strncmp ("0", get_info (sys[EJECT]),1) == 0)set_sys (sys[EJECT], 1); /* Setzen */
else /* Zurücksetzen */set_sys (sys[EJECT], 0);
break;case FILE_MAX:
printf ("Welcher Wert soll gesetzt werden : ");do{ scanf("%d", &file_max); } while (getchar()!='\n');set_sys (sys[FILE_MAX], file_max);break;
case SHARED_MAX:printf ("Welcher Wert soll gesetzt werden : ");do { scanf("%ld", &shared_max); }while (getchar()!='\n');set_sys (sys[SHARED_MAX], shared_max);break;
case 3:printf ("%s", get_info (info[CDINFO]));printf ("Weiter mit ENTER\n");getchar ();break;
case 4:printf("Programm wird beendet\n");break;
default:printf ("Unbekannte Eingabe\n");
}} while (auswahl != 4);return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o kernelinf kernelinf.c$ ./kernelinfAktueller Zustand
Zugriff auf Systeminformationen
144
4
Betriebssystem : LinuxKernelversion : 2.6.27-7-genericDatum : #1 SMP Tue Jan 4 19:33:20 UTC 2009------------------------------------Verändern können Sie folgendes ...-0- Bei "umount" CD auswerfen Aktuell:0-1- Max. Anzahl geöffneter Datei pro Prozess Aktuell:49594-2- Max. Größe des geteiltem Speicher (KB) Aktuell 33554432------------------------------------Informationen bekommen Sie zu ...-3- CD-ROM-------------------------------------4- ENDEIhre Auswahl bitte (0-4) :
Neben dem Auswerfen der CD-ROM bei umount können hier noch die maximale Anzahlgleichzeitig geöffneter Dateien pro Prozess und die maximale Größe des geteilten Speichers(Shared Memory) verändert werden. Diese Veränderungen können aber wie bereits erwähntnur mit speziellen Rechten vorgenommen werden. Informationen zum CD-ROM-Laufwerk,was es alles kann, können wieder für alle Anwender aufgelistet werden.
Bitte beachten Sie, dass diese Art von Veränderungen der Kernel-Parameter nur für den lau-fenden Betrieb gültig ist. Sobald Sie das System herunterfahren und wieder starten, sind dieWerte wieder auf ihren Ursprungszustand gestellt. Wollen Sie eine dauerhafte Veränderungbewirken, müssen Sie sich die Datei sysctl.conf im Verzeichnis /etc vornehmen. DieseDatei können Sie auch mit dem gleichnamigen Kommando sysctl verändern. Wollen Sie z. B.mit sysctl autoeject verändern, gehen Sie wie folgt vor (root-Rechte):
# sysctl -w dev.cdrom.autoeject=0dev.cdrom.autoeject = 0# sysctl -w dev.cdrom.autoeject=1dev.cdrom.autoeject = 1
Auf die Angaben von /proc/sys können Sie dabei verzichten, und anstelle eines Slashs wirdein Punkt verwendet.
Wollen Sie wissen, mit welchem Befehl der Kernel beim Booten gestartet wurde, können Sieihn mit cmdline erfragen:
$ cat /proc/cmdlineroot=/dev/hda6 vga=0x0314 hdc=ide-scsi hdclun=0 splash=silent
Oder Linux 2.6 mit LILO 22.3.4:
auto BOOT_IMAGE=2.6.4-HX ro root=301
Zu den verschiedenen Bootoptionen sei hier der Verweis auf die bootparam(7) Manual Pagegegeben. Es gibt noch weitaus mehr zu den Kernel-Informationen zu sagen, aber das würdeden Rahmen des Buches bei weitem sprengen und am Thema der Linux-Programmierung vor-beigehen.
Kernel-Informationen 4.4
145
4.4.1 /proc/locks
Mit dieser Datei werden alle Dateien angezeigt, die im Augenblick vom Kernel gesperrt wer-den (vgl. Kapitel 2 über flock() und lockf()). Diese Ausgabe enthält interne Kernel-Debug-ging-Daten und kann daher logischerweise je nach System stark variieren.
1: FLOCK ADVISORY WRITE 807 03:05:308731 0 EOF c2ac0 c0248 c2a2202: POSIX ADVISORY WRITE 708 03:05:308720 0 EOF c2a1c c2ac4 c02548
In der ersten Spalte besitzt jeder Lock eine einmalige Zahl gefolgt vom Typ des Locks. Mögli-che Ausgaben sind hier FLOCK (meist bei älteren UNIX-Datei-Locks mit dem Systemaufrufflock()) und POSIX (bei neueren POSIX-Locks mit dem Systemaufruf lockf()).
Der Wert ADVISORY der dritten Spalte bedeutet, dass ein weiterer Datenzugriff für andereBenutzer möglich ist, aber keine weiteren Lock-Versuche mehr erlaubt sind. Ein anderer Werthierfür wäre MANDATORY, was bedeutet, dass hier keine Datenzugriffe mehr möglich sind,solange der Lock vorhanden ist. In der vierten Spalte befinden sich die Lese- oder Schreib-rechte (READ, WRITE) des Eigentümers. Die fünfte Spalte enthält die PID des Lock-Eigentü-mers. Die ID der gelockten Datei finden Sie in der sechsten Spalte mit folgendem Format:
MAJOR-DEVICE:MINOR-DEVICE:INODE-NUMBER
Die Spalten sechs und sieben zeigen Anfang und Ende der gelockten Region. Im Beispiel giltdie Sperre vom Anfang 0 bis zum Ende (EOF) der Datei. Die letzten Spalten zeigen auf Kernel-interne Datenstrukturen für spezielle Debugging-Funktionen.
4.4.2 /proc/modules
In /proc/modules finden Sie eine Liste von allen Modulen, die vom System geladen wurden.Auch hierbei hängt die Ausgabe von den Einstellungen des Systems ab.
$ cat /proc/modulesisofs 40100 1 - Live 0xe0c36000udf 88356 0 - Live 0xe0c94000crc_itu_t 10112 1 udf, Live 0xe0bff000btusb 19736 0 - Live 0xe0c23000nls_iso8859_1 12032 1 - Live 0xe0c1f000nls_cp437 13696 1 - Live 0xe0c09000vfat 18816 1 - Live 0xe0c03000fat 57376 1 vfat, Live 0xe0c0f000ipv6 263972 15 - Live 0xe0c52000........
In der ersten Spalte befindet sich jeweils der Name des geladenen Moduls, gefolgt von derSpeichergröße in Bytes. In der dritten Spalte wird angezeigt, wie oft das Modul gerade benutztwird (usage count). In der letzten Spalte finden Sie die Module, die benötigt werden, damitandere Module funktionieren. Zum Beispiel benötigt das Modul cdrom das Modul ide-cd, umordentlich ausgeführt zu werden. »autoclean« gibt an, ob sich das Modul nach einer gewissenZeit selbst deaktiviert, »unused«, dass es nicht benutzt wird.
Zugriff auf Systeminformationen
146
4
4.5 Filesysteme
Weitere Informationen, die Sie im /proc-Verzeichnis finden, sind die über verschiedene File-systeme und welche dabei eingehängt (»gemountet«) sind. Das Verzeichnis /proc/filesys-tems z. B. listet alle Filesysteme auf, die dem Kernel bekannt sind und womit dieser arbeitenkann. Einige davon sind intern und können nicht gemountet werden, auch wenn sie hier auf-gelistet werden. Dazu zählt z. B. pipefs; wobei diese Auflistung nicht vollständig ist. Denn esgibt noch viele Filesysteme, die nachgeladen werden können und gerade nicht aktiv sind.Andere wiederum sind nur statisch gelinkt und werden erst bei Bedarf aktiviert.
4.5.1 /proc/mounts
Was alles gerade eingehängt ist, finden Sie in /proc/mounts verzeichnet, z. B.:
rootfs / rootfs rw 0 0/dev/root / reiserfs rw 0 0proc /proc proc rw 0 0sysfs /sys sysfs rw 0 0devpts /dev/pts devpts rw 0 0tmpfs /dev/shm tmpfs rw 0 0/dev/hda2 /C vfat rw,nodiratime,fmask=0022,dmask=0022,codepage=cp437 0 0usbdevfs /proc/bus/usb usbdevfs rw 0 0/dev/hdb /F iso9660 ro,nosuid,nodev 0 0
Die Ausgabe von /proc/mount entspricht (fast) exakt der Ausgabe von /etc/mtab, nur dass/etc/mtab von mount verwaltet wird und somit nur Dateisysteme auflistet, die mit /bin/mount eingehängt worden sind, nicht jedoch z. B. der Systemcall mount().
In der ersten Spalte einer jeden Zeile finden Sie das Gerät und in der zweiten Spalte den dazu-gehörenden Mountpoint. Der Dateisystemtyp wird in der dritten Spalte aufgelistet. Ob daraufnur lesend (ro) oder auch schreibend (rw) zugegriffen werden kann, wird – nebst anderenmöglichen Optionen – in der vierten Spalte angezeigt. Die letzten beiden Spalten sindDummy-Werte, damit das /proc/mount-Format exakt dem von /etc/mtab entspricht.
4.6 Weiterführendes
Zum /proc-Verzeichnis könnten noch viele Seiten geschrieben werden, aber irgendwo mussSchluss sein. Sie wissen jetzt, wenn Sie bestimmte Informationen für Ihr Programm oder auchzur Administration benötigen, wo und wie Sie an diese Informationen herankommen. Wei-tere Informationen zum /proc-Verzeichnis finden Sie auf der Manual Page »man 5 proc«.
Dass Linux frei im Quellcode ist (Open Source), weiß – denke ich – schon jeder. Daher hierzueinige Anwendungen, deren Quellcode zu studieren sinnvoll erscheint, da diese kräftigGebrauch vom /proc-Verzeichnis machen.
Weiterführendes 4.6
147
Anwendung Beschreibung
mount Informationen über gemountete Datenträger
ps Informationen über Prozesse
top Auslastung der CPU
xload Durchschnittliche Auslastung des Systems
xosview Durchschnittliche Auslastung des Systems, der CPU, des Speicherbedarf, Interrupts ...
Tabelle 4.3 Anwendungen, die vom /proc-Verzeichnis profitieren
349
Neben den Prozessen existiert noch eine andere Form der Programmausführung, die Linux unterstützt – die Threads, die auch als »leichtgewichtige« Prozesse bekannt sind.
10 Threads
Mit der Thread-Programmierung können Sie Anwendungen schreiben, die erheblich schnel-ler und parallel ablaufen. Sie erhalten in diesem Kapitel einen Einblick in die Thread-Program-mierung unter Linux und erfahren, wie Sie diese Kenntnisse in der Praxis einsetzen können.
$ gcc -o thserver thserver.c \-I/usr/local/include/pthread/linuxthreads \-L/usr/local/lib -llthread -llgcc_r
10.1 Unterschiede zwischen Threads und Prozessen
Prozesse wurden ja bereits ausführlich erklärt. Sie wissen, wie Sie eigene Prozesse mittelsfork() kreieren können, und mit Interprozesskommunikationen (IPC) haben Sie erfahren,wie man einzelne Prozesse synchronisiert. Den Aufwand, den Sie bei den Interprozesskom-munikationen gemacht haben, entfällt bei den Threads fast komplett.
Ein weiterer Nachteil bei der Erstellung von Prozessen gegenüber Threads ist der enorme Auf-wand, der mit der Duplizierung des Namensraumes gemacht wird – den man mit den Threadsnicht hat, da diese in einem gemeinsamen Adressraum ablaufen. Somit stehen den einzelnenThreads dasselbe Codesegment, Datensegment, der Heap und alle anderen Zustandsdaten, dieein »gewöhnlicher« Prozess besitzt, zur Verfügung – was somit auch die Arbeit beim Aus-tausch von Daten und bei der Kommunikation untereinander erheblich erleichtert. Weil aberkein Speicherschutzmechanismus unter den Threads vorhanden ist, bedeutet dies auch, wennein Thread abstürzt, reißt dieser alle anderen Threads mit.
Im ersten Moment besteht somit vorerst gar kein Unterschied zwischen einem Prozess undeinem Thread, denn letztendlich besteht ein Prozess mindestens aus einem Thread. Ferner
Hinweis
Die Beispiele im Buch verwenden Linux-Threads und sind somit nicht ohne weiteres auf anderenUNIXen lauffähig. Die BSD-Threads z. B. arbeiten zum Teil ähnlich. Es kann aber sein, dass das eineProgramm läuft und ein anderes nicht und daher auf die Linux-Threads verlinkt werden muss. Voraus-setzung sind also Linux-Threads. Speziell unter (Free)BSD müssen Sie die Linux-Threads aus den Portsinstallieren und das Programm folgendermaßen übersetzen:
Threads
350
10
endet ein Prozess, wenn sich alle Threads beenden. Somit ist der EINE Prozess (dieser eineProzess ist der erste Thread, auch »Main Thread« bzw. »Haupt-Thread« genannt) verantwort-lich für die gleichzeitige Ausführung mehrerer Threads – da doch Threads auch nur innerhalbeines Prozesses ausgeführt werden. Der gravierende Unterschied zwischen den Threads undden Prozessen besteht darin, dass Threads unabhängige Befehlsfolgen innerhalb eines Prozes-ses sind. Man könnte auch sagen, Threads sind in einem Prozess gefangen oder verkapselt –im goldenen Käfig eingeschlossen.
Natürlich müssen Sie dabei immer im Auge behalten, wenn Threads denselben Adressraumverwenden, dass sich alle Threads den statischen Speicher und somit auch die globalenVariablen miteinander teilen. Ebenso sieht dies mit den geöffneten Dateien (z. B. Filedeskrip-toren), Signalhandler- und Einstellungen, Benutzer- und Gruppenkennung und dem Arbeits-verzeichnis aus. Daher sind auch in der Thread-Programmierung gewisse Synchronisations-mechanismen nötig und auch vorhanden.
10.2 Thread-Bibliotheken
Zwar werden im Buch hier nur die Linux-Threads behandelt, dennoch sollten hier weitereBibliotheken nicht unerwähnt bleiben. Mitte der 90er-Jahre hat die Entwicklung von zahlrei-chen Thread-Bibliotheken begonnen, wobei sich letztendlich die Bibliothek von Xavier Leroyim Jahre 1997 unter dem Namen Pthread-Lib durchgesetzt hat. Diese Bibliothek wurde vonUlrich Drepper in Glibc2 an die Standardbibliothek angebunden und ist somit auf jedemLinux-System vorhanden. Nachdem Linux jetzt endlich für Mehrprozessorsysteme interessantwurde, wurden die Klagen über die Linux-Thread-Bibliothek lauter. Da es immer wieder Pro-bleme mit Signalen, der hierarchischen Beziehung zwischen Threads, der immer noch nichtganz implementierten POSIX-Konformität und noch andere Sorgen gab, war man auf derSuche nach neuen Thread-Bibliotheken.
� Einen interessanten Ansatz bietet die von Ralf S. Engelschall begonnene Bibliothek GNUPth, die bereits seit 1999 als offizielles GNU-Projekt gestartet wurde. Das »Gute« an dieserBibliothek ist, dass sie sich als eine möglichst portable Bibliothek für nicht präemptivesMultitasking eignet.
� Programmierer von Intel und IBM habe GNU Pth anschließend als Einstiegspunkt verwen-det, um daraus die Next Generation Posix Thread (NGPT) zu entwickeln, die vor allem dieSkalierung auf Mehrprozessorsysteme erhöhen sollte.
� Als Dritte im Bunde kann man die Bibliothek von Red Hat mit Native Posix Thread Libraryhervorheben, die gar eine Veränderung des Kernels nötig machte. Dabei wurden u. a. neueSystemaufrufe, ein erweiterter clone()-Aufruf und vor allem ein funktionierendes Signal-handling eingebaut.
Es sind zwar noch weitere Bibliotheken zu den Threads in der Entwicklung, aber diese hierscheinen mir am interessantesten. Da einige dieser Thread-Bibliotheken noch recht neu sindund da es immer noch ein wenig an Dokumentation dazu fehlt, macht es relativ wenig Sinn,Ihnen diese hier zu demonstrieren. Daher soll hier auf die altbewährte Linux-Thread-Biblio-thek zurückgegriffen werden.
Kernel- und User-Threads 10.3
351
10.3 Kernel- und User-Threads
Es gibt zwei Implementierungen von Threads, zum einen die Kernel-Threads, zum anderendie User-Threads. Die User-Threads sind in einer Bibliothek implementiert, die im Speicher-bereich des Benutzers ablaufen. Damit ist es möglich, Threads auch auf Betriebssystemen zuverwenden, die keine Threads unterstützen. Der Nachteil an den User-Threads ist aber, dassdie einzelnen Threads eines Prozesses nicht auf unterschiedlichen Prozessoren bei Multipro-zessorrechnern laufen. Kernel-Threads hingegen sind bereits im Betriebssystem integrierteThread-Unterstützungen. Dabei wird das Scheduling des Betriebssystems verwendet. So ist esmöglich, die einzelnen Threads eines Prozesses auf verschiedenen Prozessoren laufen zu las-sen. Linux-Threads, die in diesem Kapitel verwendet werden, unterstützen sowohl Kernel- alsauch User-Threads.
Ein wenig überraschend ist es doch, dass die User-Theads, sofern man nicht an die Multipro-zessorprogrammierung appelliert, einen gewissen Vorteil gegenüber der Kernel-Implementie-rung besitzen. Threads, die vom Kernel verwendet werden, setzen doch einige Grenzen, etwawas die Anzahl der gleichzeitigen Benutzer angeht. Aber noch ein wenig überraschender ist,dass User-Threads effizienter als Kernel-Threads ablaufen, da diese keinen extra Befehl (Soft-ware-Interrupt) zum Umschalten auf einen anderen Thread vom Kernel benötigen.
10.4 Scheduling und Zustände von Threads
Auch bei der Thread-Programmierung ist (wie bei den Prozessen) ein Scheduler entweder inder Thread-Bibliothek oder dem Betriebssystem vorhanden, der bestimmt, wann welcherThread Prozessorzeit erhält. Auch hier kann die Zuteilung wie schon bei den Prozessen prio-ritäts- und zeitgesteuert erfolgen. Bei zeitgesteuerten Threads bedeutet dies, dass jedemThread eine bestimmte Zeit (des Prozessors oder der Prozessoren) zur Verfügung steht, ehedieser automatisch unterbrochen wird und anschließend ein anderer Thread an der Reihe ist.Sind die Threads prioritätsgesteuert, so erhält der Thread mit der höchsten Priorität vom Sche-duler den Zuschlag. Außerdem wird ein laufender Thread abgebrochen, wenn ein Thread miteiner höheren Priorität ausgeführt wird. Bitte beachten Sie außerdem, wenn Sie das rein pri-oritätsgesteuerte Scheduling für die Thread-Programmierung verwenden, dass ein Thread mithöchster Priorität den Prozessor für eine uneingeschränkte Zeit verwenden und somit alleanderen Threads von der Arbeit ausschließen kann.
Bei einer User-Level-Thread-Implementierung steht Ihnen nur ein prioritätsgesteuertes Sche-duling zur Verfügung, da ein zeitgesteuertes die Verwendung von Systemcalls erfordert. BeideScheduling-Arten hingegen stehen Ihnen zur Verfügung, wenn die Thread-Bibliothek im Ker-nel des Betriebssystems implementiert wurde.
Anhand der folgenden Abbildung können Sie die Zustände erkennen, in denen sich einThread befinden kann. Bei genauerer Betrachtung fällt auf, dass sich die Threads, abgesehenvon den weiteren Unterzuständen, nicht wesentlich von den Prozessen unterscheiden.
Threads
352
10
� Bereit – Der Thread wartet, bis ihm Prozessorzeit zur Verfügung steht, um seine Arbeit aus-zuführen.
� Ausgeführt – Der Thread wird im Augenblick ausgeführt – bei Multiprozessorsystemenkönnen hierbei mehrere Threads gleichzeitig ausgeführt werden (pro CPU ein Thread).
� Wartet – Der Thread wird im Augenblick blockiert und wartet auf einen bestimmtenZustand (z. B. Bedingungsvariable, Mutex-Freigabe etc).
� Beendet – Ein Thread hat sich beendet oder wurde abgebrochen.
10.5 Die grundlegenden Funktionen zur Thread-Programmierung
10.5.1 pthread_create – einen neuen Thread erzeugen
Einen neuen Thread kann man mit der Funktion pthread_create erzeugen:
#include <pthread.h>
int pthread_create( pthread_t *thread,const pthread_attr_t *attribute,void *(*funktion)(void *),void *argumente );
Wenn Sie sich die Funktion betrachten, dürfte Ihnen die Ähnlichkeit zur Funktion clone()auffallen (siehe Manual Page), worauf sich pthread_create() unter Linux ja auch beruft.Jeder Thread bekommt eine eigene Identifikationsnummer (ID) vom Datentyp pthread_t, diein der Variablen des ersten Parameters thread abgelegt wird. Anhand dieser ID werden alleanderen Threads voneinander unterschieden. Mit dem zweiten Parameter attribute könnenSie bei dem neu zu startenden Thread Attribute setzen wie die Priorität, die Stackgröße undnoch einiges mehr. Auf die einzelnen Attribute wird noch eingegangen. Geben Sie hierfür
Abbildung 10.1 Zustände von Threads
Thread erzeugt Thread bereit
Thread in Ausführung
Thread wird blockiert
Thread wird beendetexited
canceled
Mutex
Conditon Variables
...
Hinweis
Einen Hinweis gleich zu Beginn der Thread-Programmierung – alle Funktionen aus der pthread-Bib-liothek geben bei Erfolg 0, ansonsten bei einem Fehler -1 zurück.
Die grundlegenden Funktionen zur Thread-Programmierung 10.5
353
NULL an, werden die Standardattribute für den Thread vorgenommen. Mit dem dritten Para-meter geben Sie die »Funktion« für einen Thread selbst an. Hierbei geben Sie die Anfangs-adresse einer Routine an, die der Thread verwenden soll. Wenn sich die hier angegebeneFunktion beendet, bedeutet dies auch automatisch das Ende des Threads. Argumente, die Siedem Thread mitgeben wollen, können Sie mit dem vierten Parameter argumente übergeben.Meistens wird dieser Parameter verwendet, um Daten an Threads zu übergeben. Hierzu wirdin der Praxis häufig die Adresse einer Strukturvariablen herangezogen.
Nach dem Aufruf von pthread_create() kehrt diese Funktion sofort wieder zurück und fährtmit der Ausführung hinter pthread_create() fort. Der neu erzeugte Thread führt sofort asyn-chron seine Arbeit aus. Jetzt würden praktisch zwei Threads gleichzeitig ausgeführt, derHaupt-Thread und der neue Thread, der vom Haupt-Thread mit pthread_create() erzeugtwurde. Welcher der beiden Threads hierbei zunächst mit seiner Ausführung beginnt, ist nichtfestgelegt (selbes Verhalten wie bei fork).
10.5.2 pthread_exit – einen Thread beenden
Beenden können Sie einen Thread auf unterschiedliche Weise. Meistens werden Threads mitder Funktion pthread_exit() beendet:
#include <pthread.h>
void pthread_exit( void * wert );
Diese Funktion beendet nur den Thread, indem Sie diese aufrufen. Mit dem Argument wertgeben Sie den Exit-Status des Threads an. Diesen Status können Sie mit pthread_join()ermitteln (folgt in Kürze). Natürlich darf auch hierbei, wie eben C-üblich, der Rückgabewertkein lokales Speicherobjekt vom Thread sein, da dieses (wie eben bei Funktionen auch) nachder Beendigung des Threads nicht mehr gültig ist.
Neben der Möglichkeit, einen Thread mit pthread_exit() zu beenden, sind noch folgendeDinge zu beachten:
� Ruft ein beliebiger Thread die Funktion exit() auf, werden alle Threads, einschließlich desHaupt-Threads, beendet (also das komplette Programm). Genauso sieht dies aus, wenn Siedem Prozess das Signal SIGTERM oder SIGKILL senden.
� Ein Thread, der mittels pthread_create() erzeugt wurde, lässt sich auch mit return [wert]beenden. Dies entspricht exakt dem Verhalten von pthread_exit(). Der Rückgabewertkann hierbei ebenfalls mit pthread_join() ermittelt werden.
Exit-Handler für Threads einrichtenWenn Sie einen Thread beenden, können Sie auch einen Exit-Handler einrichten. Dies wirdin der Praxis recht gerne verwendet, um z. B. temporäre Dateien zu löschen, Mutexe freizuge-ben oder eben sonstige »Reinigungsarbeiten« zu machen. Ein solcher eingerichteter Exit-Handler wird dann automatisch bei Beendigung eines Threads mit z. B. pthread_exit() oderreturn automatisch ausgeführt. Das Prinzip ist ähnlich, wie Sie es von der Standardbiblio-theksfunktion atexit() kennen sollten. Auch hierbei werden bei mehreren Exit-Handlern die
Threads
354
10
einzelnen Funktionen in umgekehrter Reihenfolge (da Stack) der Einrichtung ausgeführt. Hierdie Funktionen dazu:
#include <pthread.h>
void pthread_cleanup_push( void (*function)(void *),void *arg );
void pthread_cleanup_pop( int exec );
Eine solche Funktion richten Sie also mit der Funktion pthread_cleanup_push() ein. Als ers-ten Parameter übergeben Sie dabei die Funktion, die ausführt werden soll, und als zweitenParameter die Argumente für den Exit-Handler (falls nötig). Den zuletzt eingerichteten Exit-Handler können Sie wieder mit der Funktion pthread_cleanup_pop() vom Stack entfernen.Geben Sie allerdings einen Wert ungleich 0 als Parameter exec an, so wird diese Funktionzuvor noch ausgeführt, was bei einer Angabe von 0 nicht gemacht wird.
Etwas, was mich hier schon zur Weißglut gebracht hat, ist, dass die beiden Funktionenpthread_cleanup_push() und pthread_cleanup_pop() als Makros implementiert sind. Wasnicht so schlimm wäre, wenn pthread_cleanup_push() eine sich öffnend geschweifte Klam-mer enthält und pthread_cleanup_pop() eine sich schließende. Dies bedeutet, Sie müssenbeide Funktionen im selben Anweisungsblock ausführen. Daher müssen Sie immer ein _pushund ein _pop verwenden, auch wenn Sie wissen, dass eine _pop-Stelle nie erreicht wird.
10.5.3 pthread_join – auf das Ende eines Threads warten
Bevor Sie sich dem ersten Listing widmen können, benötigen Sie noch die Kenntnisse zurFunktion pthread_join().
#include <pthread.h>
int pthread_join( pthread_t thread, void **thread_return );
pthread_join() hält den aufrufenden Thread (meistens den Haupt-Thread), der einen Threadmit pthread_create() erzeugt hat, so lange an, bis der Thread mit der ID thread vom Typpthread_t beendet wurde. Der Exit-Status (bzw. Rückgabewert) des Threads wird an dieAdresse von thread_return geschrieben. Sind Sie nicht am Rückgabewert interessiert, kön-nen Sie hier auch NULL verwenden. pthread_join() ist also das, was Sie bei den Prozessen mitwaitpid() kennen.
Ein Thread, der sich beendet, wird eben so lange nicht »freigegeben« bzw. als beendeterThread anerkannt, bis ein anderer Thread pthread_join() aufruft. Diesen Zusammenhangkönnen Sie bei den Prozessen mit »Zombie-Prozessen« vergleichen. Daher sollte man fürjeden erzeugten Thread einmal pthread_join() aufrufen, es sei denn, man hat einen Thread»abgehängt« (aber dazu in Kürze mehr).
10.5.4 pthread_self – die ID von Threads ermitteln
Die Identifikationsnummer eines auszuführenden Threads können Sie sich mit der Funktionpthread_self() erfragen.
Die grundlegenden Funktionen zur Thread-Programmierung 10.5
355
#include <pthread.h>
pthread_t pthread_self(void);
Als Rückgabewert erhalten Sie die Thread-ID vom Datentyp pthread_t.
Hierzu soll nun ein einfaches Beispiel erstellt werden, das alle bisher vorgestellten Funktionenin klarer Weise demonstrieren soll.
/* thread1.c */#include <stdio.h>#include <stdlib.h>#include <pthread.h>/* insg. MAX_THREADS Threads erzeugen */#define MAX_THREADS 3#define BUF 255
/* Einfache Daten für die Wertübergabe an den Thread */struct data {
int wert;char msg[BUF];
};
/* Ein einfacher Exit-Handler für Threads, der ** pthread_cleanup_push und pthread_cleanup_pop ** in der Praxis demonstrieren soll */static void exit_handler_mem( void * arg ) {
printf("\tExit-Handler aufgerufen ...");struct data *mem = (struct data *)arg;/* Speicher freigeben */free(mem);printf("Speicher freigegeben\n");
}
/* Die Thread-Funktion */static void mythread (void *arg) {
struct data *f = (struct data *)arg;/* Exit-Handler einrichten - wird automatisch nach ** pthread_exit oder Thread-Ende aufgerufen */pthread_cleanup_push( exit_handler_mem, (void*)f );/* Daten ausgeben */printf("\t-> Thread mit ID:%ld gestartet\n",
pthread_self());printf("\tDaten empfangen: \n");printf("\t\twert = \"%d\"\n", f->wert);printf("\t\tmsg = \"%s\"\n", f->msg);/* Den Exit-Handler entfernen, aber trotzdem ausführen, ** da als Angabe 1 anstatt 0 verwendet wurde */pthread_cleanup_pop( 1 );/* Thread beenden - Als Rückgabewert Thread-ID verwenden.* Alternativ kann hierfür auch:
Threads
356
10
* return(void) pthread_self();* verwendet werden */
pthread_exit((void *)pthread_self());}
int main (void) {pthread_t th[MAX_THREADS];struct data *f;int i;static int ret[MAX_THREADS];/* Haupt-Thread gestartet */printf("\n-> Main-Thread gestartet (ID:%ld)\n",
pthread_self());/* MAX_THREADS erzeugen */for (i = 0; i < MAX_THREADS; i++) {
/* Speicher für Daten anfordern u. m. Werten belegen*/f = (struct data *)malloc(sizeof(struct data));if(f == NULL) {
printf("Konnte keinen Speicher reservieren ...!\n");exit(EXIT_FAILURE);
}/* Zufallszahl zwischen 1 und 10 (Spezial) */f->wert = 1+(int) (10.0*rand()/(RAND_MAX+1.0));snprintf (f->msg, BUF, "Ich bin Thread Nr. %d", i+1);/* Jetzt Thread erzeugen */if(pthread_create(&th[i], NULL, &mythread, f) != 0) {
fprintf (stderr, "Konnte Thread nicht erzeugen\n");exit (EXIT_FAILURE);
}}/* Auf das Ende der Threads warten */for( i=0; i < MAX_THREADS; i++)
pthread_join(th[i], &ret[i]);/* Rückgabewert der Threads ausgeben */for( i=0; i < MAX_THREADS; i++)
printf("<-Thread %ld ist fertig\n", ret[i]);/* Haupt-Thread ist jetzt auch fertig */printf("<- Main-Thread beendet (ID:%ld)\n",
pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread1 thread1.c -lpthread$ ./thread1
-> Main-Thread gestartet (ID:-1209412512)-> Thread mit ID:-1209414736 gestartetDaten empfangen:
wert = "9"
Die grundlegenden Funktionen zur Thread-Programmierung 10.5
357
msg = "Ich bin Thread Nr. 1"Exit-Handler aufgerufen ... Speicher freigegeben-> Thread mit ID:-1217807440 gestartetDaten empfangen:
wert = "4"msg = "Ich bin Thread Nr. 2"
Exit-Handler aufgerufen ... Speicher freigegeben-> Thread mit ID:-1226200144 gestartetDaten empfangen:
wert = "8"msg = "Ich bin Thread Nr. 3"
Exit-Handler aufgerufen ...Speicher freigegeben<-Thread –1209414736 ist fertig<-Thread –1217807440 ist fertig<-Thread –1226200144 ist fertig<- Main-Thread beendet (ID:-1209412512)
Dieses Beispiel demonstriert auch auf einfache Weise, wie Sie Daten an einen neu erzeugtenThread übergeben können (hier mit der Struktur data). Ebenfalls gezeigt wurde hier die Ver-wendung eines Exit-Handlers, der nur den im Haupt-Thread angeforderten Speicherbereichfreigibt. Zugegeben, das ließe sich auch im Thread »mythread« einfacher realisieren, aber zuAnschauungszwecken sind solch einfache Codebeispiele immer noch am besten. Im Beispielwurden außerdem drei Threads »mythread« erzeugt, die im Prinzip alle dasselbe machen,nämlich eine einfache Ausgabe der Daten, die an die Threads übergeben wurden. Hierbeimuss nochmals explizit darauf hingewiesen werden, dass die Ausführung, in welcher Reihen-folge die Threads starten, nicht vorgegeben ist, auch wenn dies hier einen anderen Anscheinmacht. Hierzu werden Synchronisationsmechanismen erforderlich. Jeder Thread wurde hiermit pthread_exit() und der eignen Thread-ID als Rückgabewert beendet. Genauso gut kanndies natürlich auch mit return gemacht werden. Der Rückgabewert von den einzelnenThreads wird im Haupt-Thread von pthread_join() erwartet und ausgegeben. Der Haupt-Thread beendet sich am Ende erst, wenn alle Threads fertig sind.
Würden Sie in diesem Beispiel pthread_join() weglassen, so würde sich der Haupt-Threadnoch vor den anderen Threads beenden. Dies bedeutet, dass alle anderen Threads zwar nochlaufen, aber auf nun nicht mehr gültige Strukturvariablen zugreifen würden.
Rückgabewert von ThreadsZwar wurde schon auf den Rückgabewert von Threads eingegangen, aber hierbei wurden nurThread-spezifische Daten zurückgegeben (hier die Thread-ID). Aber genauso wie schon beider Wertübergabe an Threads können Sie hierbei auch ganze Strukturen zurückgeben, was inder Praxis auch häufig so der Fall ist. Hierzu ein ähnliches Beispiel wie schon »thread1.c«, nur
Hinweis am Rand
Bevor sich jemand über die Warnmeldung des Compilers wundert, noch ein Satz zum Casten vonvoid *. In der Programmiersprache C ist ein Casten von oder nach void * nicht nötig. Aber wen dieWarnmeldung stört, der kann dies gerne trotzdem nachholen.
Threads
358
10
dass jetzt die Daten der Struktur aus dem Thread zurückgegeben und im Haupt-Thread mitpthread_join() »abgefangen« und anschließend ausgegeben werden. Auf die Verwendungeines Exit-Handlers wurde der Übersichtlichkeit halber zuliebe verzichtet.
/* thread2.c */#include <stdio.h>#include <stdlib.h>#include <pthread.h>/* insg. MAX_THREADS Threads erzeugen */#define MAX_THREADS 3#define BUF 255
/* Einfache Daten für die Wertübergabe an den Thread */struct data {
int wert;char msg[BUF];
};
/* Die Thread-Funktion */static void *mythread (void *arg) {
struct data *f= (struct data *)arg;/* Zufallszahl zwischen 1 und 10 (Spezial) */f->wert = 1+(int) (10.0*rand()/(RAND_MAX+1.0));snprintf (f->msg, BUF, "Ich bin Thread Nr. %ld",
pthread_self());/* Thread beenden - Als Rückgabewert Strukturdaten* verwenden - Alternativ auch pthread_exit( f ); */
return arg;}
int main (void) {pthread_t th[MAX_THREADS];int i;struct data *ret[MAX_THREADS];
/* Haupt-Thread gestartet */printf("\n-> Main-Thread gestartet (ID:%ld)\n",
pthread_self());/* Speicher reservieren */for (i = 0; i < MAX_THREADS; i++){
ret[i] = (struct data *)malloc(sizeof(struct data));if(ret[i] == NULL) {
printf("Konnte keinen Speicher reservieren ...!\n");exit(EXIT_FAILURE);
}}/* MAX_THREADS erzeugen */for (i = 0; i < MAX_THREADS; i++) {
/* Jetzt Thread erzeugen */if(pthread_create(&th[i],NULL,&mythread,ret[i]) !=0) {
Die grundlegenden Funktionen zur Thread-Programmierung 10.5
359
fprintf (stderr, "Konnte Thread nicht erzeugen\n");exit (EXIT_FAILURE);
}}/* Auf das Ende der Threads warten */for( i=0; i < MAX_THREADS; i++)
pthread_join(th[i], (void **)&ret[i]);
/* Daten ausgeben */for( i=0; i < MAX_THREADS; i++) {
printf("Main-Thread: Daten empfangen: \n");printf("\t\twert = \"%d\"\n", ret[i]->wert);printf("\t\tmsg = \"%s\"\n", ret[i]->msg);
}/* Haupt-Thread ist jetzt auch fertig */printf("<- Main-Thread beendet (ID:%ld)\n",
pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread2 thread2.c -lpthread$ ./thread2
-> Main-Thread gestartet (ID:-1209412512)Main-Thread: Daten empfangen:
wert = "9"msg = "Ich bin Thread Nr. –1209414736"
Main-Thread: Daten empfangen:wert = "4"msg = "Ich bin Thread Nr. –1217807440"
Main-Thread: Daten empfangen:wert = "8"msg = "Ich bin Thread Nr. –1226200144"
<- Main-Thread beendet (ID:-1209412512)
10.5.5 pthread_equal – die ID von zwei Threads vergleichen
Um einen Thread mit einem anderen Thread zu vergleichen, kann die Funktionpthread_equal() verwendet werden. Dies wird häufig verwendet, um sicherzugehen, dassnicht ein Thread gleich derselbe ist. Ein Wert ungleich 0 wird zurückgegeben, wenn beideThreads gleich sind, und 0 wird zurückgegeben, wenn die Threads eine unterschiedliche Iden-tifikationsnummer (ID) besitzen.
Das folgende Beispiel erzeugt drei Threads mit derselben »Funktion«, hierbei soll jeder Threadwiederum eine andere Aktion ausführen. Im Beispiel ist dies zwar nur die Ausgabe eines Tex-tes, aber in der Praxis könnten Sie hierbei neue Funktionen aufrufen. Für die ersten dreiThreads wird jeweils eine bestimmte Aktion festgelegt. Alle anderen Threads führen nur nochdie else-Aktion aus. Dies ist z. B. sinnvoll, wenn Sie in Ihrer Anwendung Vorbereitungen tref-
Threads
360
10
fen wollen (im Beispiel eben drei Vorbereitungen) so wie Dateien anlegen, Müll beseitigen,eine Server-Verbindung herstellen und noch vieles mehr. Sind diese Vorbereitungen getrof-fen, wird immer mit der gleichen Funktion fortgefahren. Damit der Vergleich von Threadsmit pthread_equal() auch funktioniert, wurden die Thread-IDs, die beim Anlegen mitpthread_create() erzeugt worden sind, in globale Variablen gespeichert – und sind daherauch für alle Threads »sichtbar«. Hier das Beispiel, dessen Ausgabe eigentlich auch einigeserklärt.
/* thread3.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#define MAX_THREADS 5#define BUF 255
/* Globale Variable mit Thread-IDs ** für alle Threads sichtbar */
static pthread_t th[MAX_THREADS];
static void aktion(void *name) {while( 1 ) {
if(pthread_equal(pthread_self(),th[0])) {printf("\t->(%ld): Aufgabe \"abc\" Ausführen \n",
pthread_self());break;
}else if(pthread_equal(pthread_self(),th[1])) {
printf("\t->(%ld): Aufgabe \"efg\" Ausführen \n",pthread_self());
break;}else if(pthread_equal(pthread_self(),th[2])) {
printf("\t->(%ld): Aufgabe \"jkl\" Ausführen \n",pthread_self());
break;}else {
printf("\t->(%ld): Aufgabe \"xyz\" Ausführen \n",pthread_self());
break;}
}pthread_exit((void *)pthread_self());
}
int main (void) {int i;static int ret[MAX_THREADS];
Die grundlegenden Funktionen zur Thread-Programmierung 10.5
361
printf("->Haupt-Thread (ID:%ld) gestartet...\n",pthread_self());
/* Threads erzeugen */for (i = 0; i < MAX_THREADS; i++) {
if (pthread_create (&th[i],NULL,&aktion,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 thread3 thread3.c -lpthread$ ./thread3->Haupt-Thread (ID:-1209412512) gestartet...
->(-1209414736): Aufgabe "abc" Ausführen->(-1217807440): Aufgabe "efg" Ausführen->(-1226200144): Aufgabe "jkl" Ausführen->(-1234592848): Aufgabe "xyz" Ausführen->(-1242985552): Aufgabe "xyz" Ausführen<-Thread -1209414736 mit Arbeit fertig<-Thread -1217807440 mit Arbeit fertig<-Thread -1226200144 mit Arbeit fertig<-Thread -1234592848 mit Arbeit fertig<-Thread -1242985552 mit Arbeit fertig
->Haupt-Thread (ID:-1209412512) fertig ...
Sie können daran erkennen, dass die ersten drei Threads jeweils »abc«, »efg« und »jkl« ausfüh-ren. Alle noch folgenden Threads führen dann »xyz« aus. Zugegeben, das lässt sich elegantermit den Synchronisationsmechanismen der Thread-Bibliothek lösen, aber das Beispieldemonstriert den Sachverhalt der Funktion pthread_equal() recht gut.
10.5.6 pthread_detach – einen Thread unabhängig machen
Das Gegenteil von pthread_join() stellt die Funktion pthread_detach() dar. Mit dieserFunktion legen Sie fest, dass nicht mehr auf die Beendigung des Threads gewartet werden soll.
#include <pthread.h>
int pthread_detach( pthread_t thread );
Threads
362
10
Sie lösen hiermit praktisch den Thread mit der ID thread von der Hauptanwendung los. Siekönnen diesen Vorgang gerne mit den Daemon-Prozessen vergleichen. Dass dieser Threaddann selbstständig ist, ist nichts Magisches, im Grunde »markieren« Sie den Thread damit nur,so dass bei seinem Beenden der Exit-Status und die Thread-ID gleich freigegeben werden.Ohne pthread_detach() würde dies erst der Fall nach einem pthread_join-Aufruf sein.Natürlich bedeutet die Verwendung von pthread_detach(), dass hierbei auch keinpthread_join() mehr auf das Ende des Threads reagiert.
Ein typischer Codeausschnitt, wie Sie einen Thread von den anderen loslösen können, siehtwie folgt aus:
pthread_t a_thread;int ret;.../* Einen neuen Thread erzeugen */ret = pthread_create( &a_thread, NULL,
thread_function, NULL);/* bei Erfolg den Thread abhängen ... */if (ret == 0) {
pthread_detach(a_thread);}
10.6 Die Attribute von Threads und das Scheduling
Wie Sie bereits im Abschnitt zuvor erfahren haben, kann man auch das AttributPTHREAD_CREATE_DETACHED zum Abhängen (detached) von Threads verwenden. Hierzu kön-nen 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 initiali-sieren. 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
Hinweis
Ein Thread, der mit pthread_detach() oder dem Attribut PTHREAD_CREATE_DETACHED von denanderen Threads losgelöst wurde, kann nicht mehr mit pthread_join() abgefangen werden. DerThread läuft praktisch ohne äußere Kontrolle weiter.
Die Attribute von Threads und das Scheduling 10.6
363
Thread nach dem Exit-Status diese Threads fragt (mit pthread_join()). Mit der Funktionpthread_attr_getdetachestate() können Sie das »detached«-Attribut erfragen, und mitpthread_attr_setdetachedstate() wird es gesetzt. Neben dem eben erwähntenPTHREAD_CREATE_JOINABLE, was ja auch die Standardeinstellung eines erzeugten Threads ist,können Sie hierbei auch PTHREAD_CREATE_DETACHED verwenden. Das Setzen vonPTHREAD_CREATE_DETACHED entspricht exakt dem Verhalten der Funktion pthread_detach()(siehe Abschnitt 10.5.6) und kann auch stattdessen verwendet werden – da es erheblichkürzer ist. Benötigen Sie das Attributobjekt attr nicht mehr, können Sie es mitpthread_attr_destroy() löschen. Somit machen die Funktionen wohl erst Sinn, wenn Siebereits mit pthread_detach() einen Thread ausgehängt haben und diesen eventuell wiederzurückholen (PTHREAD_CREATE_JOINABLE) müssen.
Bedeutend wichtiger im Zusammenhang mit den Attributen von Threads erscheint hier schondas Setzen der Prozessorzuteilung (Scheduling). Laut POSIX gibt es drei verschiedene solcherProzesszuteilungen (Scheduling Policies):
� SCHED_OTHER – Die normale Priorität wie bei einem gewöhnlichen Prozess. Der Threadwird 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 Prio-rität ausgestattet ist.
� Echtzeit (SCHED_FIFO) – Dies sind Echtzeitprozesse. Sie werden in jedem Fall SCHED_OTHER-Prozessen vorgezogen. Auch können sie nicht von normalen Prozessen unterbrochen wer-den. 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.
� Echtzeit (SCHED_RR) – Dies sind Round-Robin-Echtzeitprozesse. Beim Round-Robin-Verfah-ren hat jeder Prozess die gleiche Zeitspanne zur Verfügung. Ist diese verstrichen, so kommtder nächste Prozess an die Reihe. Unter Linux werden diese Prozesse genauso behandeltwie die Echtzeitprozesse, mit dem Unterschied, dass diese an das Ende der run-queuegesetzt werden, wenn sie den Prozessor verlassen.
Jetzt habe ich hier Echtzeitoperationen ins Spiel geworfen und sollte daher hierzu einen kur-zen 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, son-dern auch hier muss man sich damit begnügen, dass diese innerhalb einer vorgegebenen Zeit-spanne abgearbeitet werden. Allerdings müssen solche Echtzeitoperationen auch unterbrech-bar sein, um auf plötzliche unvorsehbare Ereignisse reagieren zu können. Daher unterscheidetman hier zwischen »weichen« und »harten« Echtzeitanforderungen. Die Anforderungen hän-gen vom Anwendungsfall ab, so kann man bei einem Computerspiel jederzeit »weiche« Echt-zeitanforderungen setzen – was bei Maschinenanforderungen wohl eher katastrophal seinkann. Hier muss innerhalb einer vorgegebenen Zeit reagiert werden. Der Hauptbereich vonEchtzeitanwendungen ist immer noch:
� Multimedia – Audio, Video
� Steuerung, Regelung – Maschinen-, Robotersteuerung
Threads
364
10
Damit eine solche Zuteilungsstrategie auch funktioniert, muss das System diese auchunterstützen. Dies ist gegeben, wenn bei Ihnen die Konstante _POSIX_THREAD_
PRIORITY_SCHEDULING definiert ist. Beachten Sie außerdem, dass die Echtzeit-Zuteilungsstra-tegien 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 einesThreads mit der ID thread vom Typ pthread_t. Die Strategie legen Sie mit dem Parameterpolicy fest. Hierbei kommen die bereits beschriebenen Konstanten SCHED_OTHER, SCHED_FIFOund SCHED_RR in Frage. Mit dem letzten Parameter der Struktur sched_param, die sich in derHeaderdatei <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 Pri-orität zu verändern. Sie finden hierbei zwei Funktionen, eine, womit Sie die Strategie und Pri-orität abfragen können, und eine weitere, womit Sie diese Werte neu setzen können. Aller-dings benötigen Sie für das Setzen Superuser-root-Rechte, was im Beispiel ebenfalls ermitteltwird.
/* 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, ¶m)) == 0 ) {
printf("Zuteilung: ");switch( policy ) {
case SCHED_OTHER : printf("SCHED_OTHER; "); break;case SCHED_FIFO : printf("SCHED_FIFO; "); break;
Die Attribute von Threads und das Scheduling 10.6
365
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, ¶m)) != 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");
Threads
366
10
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 ...$ suPassword:********# ./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 Funktionenauch ü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);
Threads synchronisieren 10.7
367
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 (Zuteilungsstra-tegie und Priorität) vom Erzeuger-Thread übernehmen soll, stehen Ihnen folgende Funktio-nen 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_set-inheritsched() 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 Priori-tä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ätfestgelegt ist. Wurden die Attribute des »Eltern«-Threads nicht verändert, so ist der Kind-Thread dennoch (logischerweise) mit denselben Attributen wie der »Eltern«-Thread ausgestat-tet – da Standardattribute.
10.7 Threads synchronisieren
In vielen Fällen, eigentlich bei Threads fast immer, werden ja mehrere parallel laufende Pro-zesse benötigt, die gemeinsame Daten verwenden und/oder austauschen. Einfachstes Beispiel,ein Thread schreibt gerade etwas in eine Datei, während ein anderer Thread daraus etwasliest. Selbes Problem haben Sie auch beim Zugriff auf globale Variablen. Wenn mehrereThreads darauf zugreifen müssen und Sie hierbei keine Vorkehrungen getroffen haben, istnicht vorherzusagen, welcher Thread die Variable gerade bearbeitet. Sind hierbei z. B. mathe-matische Arbeiten auf mehreren Threads aufgeteilt, kann man mit fast 100 %iger Sicherheitsagen, dass das Ergebnis nicht richtig sein wird.
Hierfür sei folgendes einfaches Beispiel gegeben. Zwei Threads greifen auf eine globale Vari-able zu – hier auf einen geöffneten FILE-Zeiger. Ein Thread wird erzeugt, um etwas in dieseDatei zu schreiben, und ein weiterer Thread soll diese wieder auslesen. Ein simples Beispiel,wie es scheint, nur dass es hierbei schon zu Synchronisationsproblemen (Race Conditions)kommt. Aber testen Sie selbst.
Threads
368
10
/* thread5.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>
#define MAX_THREADS 2#define BUF 255#define COUNTER 10000000
/* Globale Variable */static FILE *fz;
static void open_file(const char *file) {fz = fopen( file, "w+" );if( fz == NULL ) {
printf("Konnte Datei %s nicht öffnen\n", file);exit(EXIT_FAILURE);
}}
static void thread_schreiben(void *name) {char string[BUF];
printf("Bitte Eingabe machen: ");fgets(string, BUF, stdin);fputs(string, fz);fflush(fz);/* Thread-Ende */pthread_exit((void *)pthread_self());
}
static void thread_lesen(void *name) {char string[BUF];rewind(fz);fgets(string, BUF, fz);printf("Ausgabe Thread %ld: ", pthread_self());fputs(string, stdout);fflush(stdout);/* Thread-Ende */pthread_exit((void *)pthread_self());
}
int main (void) {static pthread_t th1, th2;static int ret1, ret2;
printf("->Haupt-Thread (ID:%ld) gestartet ...\n",pthread_self());
open_file("testfile");
Threads synchronisieren 10.7
369
/* Threads erzeugen */if (pthread_create( &th1, NULL,
&thread_schreiben, NULL)!=0) {fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}/* Threads erzeugen */if (pthread_create(&th2,NULL,&thread_lesen,NULL) != 0) {
fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}
pthread_join(th1, &ret1);pthread_join(th2, &ret2);
printf("<-Thread %ld fertig\n", th1);printf("<-Thread %ld fertig\n", th1);printf("<-Haupt-Thread (ID:%ld) fertig ...\n",
pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread5 thread5.c -lpthread$ ./thread5->Haupt-Thread (ID:-1209412512) gestartet...Bitte Eingabe machen: Ausgabe Thread -1217807440: Hallo, das ist ein Test<-Thread -1209414736 fertig<-Thread -1209414736 fertig<-Haupt-Thread (ID:-1209412512) fertig ...$ cat testfileHallo, das ist ein Test
Bei der Eingabe können Sie schon erkennen, dass der Thread »thread_lesen« schon mit seinerAusgabe begonnen hat und sich schon wieder beendet hat, bevor Sie etwas von der Tastatureingeben konnten. Richtig ausgeführt sollte hier folgende Ausgabe bei der Programmausfüh-rung entstehen:
$ ./thread5->Haupt-Thread (ID:-1209412512) gestartet ...Bitte Eingabe machen: Hallo WeltAusgabe Thread -1217807440: Hallo Welt<-Thread -1209414736 fertig<-Thread -1209414736 fertig<-Haupt-Thread (ID:-1209412512) fertig ...
Zugegeben, als echter C-Guru würde Ihnen jetzt hier schon etwas einfallen, z. B. eine »pol-lende« Schleife mit einem sleep() um den Lese-Thread herumzubauen, die immer wiederabfragt, ob fgets() etwas eingelesen hat. Na ja, das wäre wohl nicht im Sinne des Erfinders,und wenn Sie wirklich die Threads für Echtzeitanwendungen verwenden wollen, ist das wohlauch das Ende Ihrer Programmiererkarriere, wenn die Weichenschaltung einer U-Bahn »in
Threads
370
10
einer pollenden Schleife« warten muss, bevor diese gestellt werden kann! Und außerdem gibtes für solche Fälle einige Synchronisationsmöglichkeiten, die Ihnen die Thread-Bibliothekanbietet.
10.7.1 Mutexe
Wenn Sie mehrere Threads starten und diese quasi parallel ablaufen, können Sie nicht erken-nen, wie weit welcher Thread gerade mit der Verarbeitung von Daten ist. Wenn mehrereThreads beispielsweise an ein und derselben Aufgabe abhängig voneinander arbeiten, wirdeine Synchronisation erforderlich. Genauso ist dies erforderlich, wenn Threads globale Vari-ablen oder die Hardware wie z. B. die Tastatur (stdin) verwenden, da sonst ein Thread dieseVariable einfach überschreiben würde, bevor sie noch verwendet wird.
Um Threads zu synchronisieren, haben Sie zwei Möglichkeiten, zum einen mit so genanntenLocks, die Sie in diesem Kapitel mit den Mutexen durchgehen werden, und zum anderen miteinem Monitor. Mit dem Monitor werden so genannte Condition-Variablen verwendet.
Die Funktionsweise von Mutexen ähnelt den Semaphoren bei den Prozessen. Genauer noch:Ein Mutex ist nichts weiter als ein Semaphor, was wiederum nur eine atomare Operation aufeine Variable ist. Trotzdem lassen sich diese aber wesentlich einfacher erstellen. Das Prinzipist simpel. Ein Thread arbeitet mit einer globalen oder statischen Variablen, die für alle ande-ren Threads von einem Mutex blockiert (gesperrt) wird. Benötigt der Thread diese Variablenicht mehr, gibt er diese frei.
Anhand dieser Erklärung dürfte auch klar sein, dass man selbst dafür verantwortlich ist, kei-nen Deadlock zu erzeugen. In folgenden Fällen könnten auch bei Threads Deadlocks auftre-ten:
� Threads können Ressourcen anfordern, obwohl sie bereits Ressourcen besitzen.
� Ein Thread gibt seine Ressource nicht mehr frei.
� Eine Resource ist frei oder im Besitz eines »exklusiven« Threads.
Hinweis
Der Begriff »Mutex« steht für »Mutual Exclusion Device« (= gegenseitiger Ausschluss). Ein Mutex istsomit ohne Besitzer oder gehört genau einem Thread.
Abbildung 10.2 Nur ein Thread kann einen Mutex sperren.
Globale Variable
Mutex
Thread A Thread B
gesperrt blockiert
Zugriff
Threads synchronisieren 10.7
371
Im Falle eines Deadlocks kann keiner der beteiligten Threads seine Arbeit mehr fortsetzen,und somit ist meist keine normale Beendigung mehr möglich. Datenverlust kann die Folgesein.
Statische MutexeUm eine Mutex-Variable als statisch zu definieren, müssen Sie diese mit der KonstantePTHREAD_MUTEX_INITIALIZER initialisieren. Folgende Funktionen stehen Ihnen zur Verfü-gung, um Mutexe zu sperren und wieder freizugeben:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);
Mit pthread_mutex_lock() sperren Sie einen Mutex. Wenn hierbei z. B. ein Thread versucht,mit demselben Mutex ebenfalls eine Sperre einzurichten, so wird dieser so lange blockiert, bisder Mutex von einem anderen Thread wieder mittels pthread_mutex_unlock() freigegebenwird.
Die Funktion pthread_mutex_trylock() ist ähnlich wie pthread_mutex_lock(), nur dassdiese Funktion den aufrufenden Thread nicht blockiert, wenn ein Mutex durch einen anderenThread blockiert wird. pthread_mutex_trylock() kehrt stattdessen mit dem Fehlercode(errno) EBUSY zurück und macht mit der Ausführung des aufrufenden Threads weiter.
Das folgende Beispiel ist dasselbe, wie Sie es schon vom Listing »thread5.c« her kennen, nurdass jetzt das Synchronisationsproblem mithilfe eines Mutex behoben wird. Zuerst wird glo-bal der Mutex mit der Konstante PTHREAD_MUTEX_INITIALIZER statisch initialisiert, undanschließend werden im Beispiel die Sperren dort gesetzt und wieder freigegeben, wo diessinnvoll ist.
/* thread6.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>
#define MAX_THREADS 2#define BUF 255#define COUNTER 10000000
static FILE *fz;
/* Statische Mutex-Variable */pthread_mutex_t fz_mutex=PTHREAD_MUTEX_INITIALIZER;
static void open_file(const char *file) {fz = fopen( file, "w+" );if( fz == NULL ) {
printf("Konnte Datei %s nicht öffnen\n", file);
Threads
372
10
exit(EXIT_FAILURE);}
}
static void thread_schreiben(void *name) {char string[BUF];
printf("Bitte Eingabe machen: ");fgets(string, BUF, stdin);fputs(string, fz);fflush(fz);
/* Mutex wieder freigeben */pthread_mutex_unlock( &fz_mutex );
/* Thread-Ende */pthread_exit((void *)pthread_self());
}
static void thread_lesen(void *name) {char string[BUF];
/* Mutex sperren */pthread_mutex_lock( &fz_mutex );rewind(fz);fgets(string, BUF, fz);printf("Ausgabe Thread %ld: ", pthread_self());fputs(string, stdout);fflush(stdout);
/* Mutex wieder freigeben */pthread_mutex_unlock( &fz_mutex );
/* Thread-Ende */pthread_exit((void *)pthread_self());
}
int main (void) {static pthread_t th1, th2;static int ret1, ret2;
printf("->Haupt-Thread (ID:%ld) gestartet ...\n",pthread_self());
open_file("testfile");
/* Mutex sperren */pthread_mutex_lock( &fz_mutex );
Threads synchronisieren 10.7
373
/* Threads erzeugen */if( pthread_create( &th1, NULL, &thread_schreiben,
NULL)!=0) {fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}/* Threads erzeugen */if(pthread_create(&th2,NULL, &thread_lesen, NULL) != 0) {
fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}pthread_join(th1, &ret1);pthread_join(th2, &ret2);
printf("<-Thread %ld fertig\n", th1);printf("<-Thread %ld fertig\n", th1);printf("<-Haupt-Thread (ID:%ld) fertig ...\n",
pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread6 thread6.c -lpthread$ ./thread6->Haupt-Thread (ID:-1209412512) gestartet ...Bitte Eingabe machen: Hallo Welt mit MutexeAusgabe Thread -1217807440: Hallo Welt mit Mutexe<-Thread -1209414736 fertig<-Thread -1209414736 fertig<-Haupt-Thread (ID:-1209412512) fertig ...
Natürlich können Sie den Lese-Thread mit pthread_mutex_trylock() als eine nicht blockie-rende Mutex-Anforderung ausführen. Hierzu müssten Sie nur die Funktion »thread_lesen« einwenig umändern. Hier ein solcher möglicher Ansatz:
static void thread_lesen(void *name) {char string[BUF];
/* Versuche Mutex zu sperren */while( (pthread_mutex_trylock( &fz_mutex )) == EBUSY) {
sleep(10);printf("Lese-Thread wartet auf Arbeit ...\n");printf("Bitte Eingabe machen: ");fflush(stdout);
}rewind(fz);fgets(string, BUF, fz);printf("Ausgabe Thread %ld: ", pthread_self());fputs(string, stdout);fflush(stdout);
Threads
374
10
/* Mutex wieder freigeben */pthread_mutex_unlock( &fz_mutex );
/* Thread-Ende */pthread_exit((void *)pthread_self());
}
Hierbei wird versucht, alle zehn Sekunden den Mutex zu sperren. Solange EBUSY zurückgege-ben wird, ist der Mutex noch von einem anderen Thread gesperrt. Während dieser Zeitkönnte der wartende Thread ja andere Arbeiten ausführen (es gibt immer was zu tun). DasProgramm bei der Ausführung mit pthread_mutex_trylock():
$ gcc -o thread7 thread7.c -lpthread$ ./thread7->Haupt-Thread (ID:-1209412512) gestartet ...Bitte Eingabe machen: Lese-Thread wartet auf Arbeit ...Bitte Eingabe machen: Lese-Thread wartet auf Arbeit ...Bitte Eingabe machen: Hallo Mutex, Du bist freiAusgabe Thread -1217807440: Hallo Mutex, Du bist frei<-Thread -1209414736 fertig<-Thread -1209414736 fertig<-Haupt-Thread (ID:-1209412512) fertig ...
Dynamische MutexeWenn Sie Mutexe in einer Struktur verwenden wollen, was durchaus eine gängige Praxis ist,können Sie dynamische Mutexe verwenden. Dies sind dann Mutexe, für die zur Laufzeit mitz. B. malloc() Speicher angefordert wird. Für dynamische Mutexe stehen folgende Funktio-nen zur Verfügung:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr );
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Mit pthread_mutex_init() initialisieren Sie das Mutex mutex. Mit dem Parameter mutexattrkönnen Sie Attribute für das Mutex verwenden. Wird hierbei NULL angegeben, werden dieStandardattribute verwendet. Auf die Attribute von Mutexen wird in Kürze eingegangen.Freigeben können Sie einen solchen dynamisch angelegten Mutex wieder mitpthread_mutex_destroy(). Hierzu nochmals dasselbe Beispiel wie eben mit »thread6.c«, nurmit dynamisch angelegtem Mutex.
/* thread8.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <pthread.h>#define BUF 255
Threads synchronisieren 10.7
375
struct data {FILE *fz;char filename[BUF];pthread_mutex_t mutex;
};
static void thread_schreiben(void *arg) {char string[BUF];struct data *d=(struct data *)arg;
printf("Bitte Eingabe machen: ");fgets(string, BUF, stdin);fputs(string, d->fz);fflush(d->fz);
/* Mutex wieder freigeben */pthread_mutex_unlock( &d->mutex );/* Thread-Ende */pthread_exit((void *)pthread_self());
}
static void thread_lesen(void *arg) {char string[BUF];struct data *d=(struct data *)arg;
/* Mutex sperren */while( (pthread_mutex_trylock( &d->mutex )) == EBUSY) {
sleep(10);printf("Lese-Thread wartet auf Arbeit ...\n");printf("Bitte Eingabe machen: ");fflush(stdout);
}rewind(d->fz);fgets(string, BUF, d->fz);printf("Ausgabe Thread %ld: ", pthread_self());fputs(string, stdout);fflush(stdout);/* Mutex wieder freigeben */pthread_mutex_unlock( &d->mutex );/* Thread-Ende */pthread_exit((void *)pthread_self());
}
int main (void) {static pthread_t th1, th2;static int ret1, ret2;struct data *d;
/* Speicher für die Struktur reservieren */d = malloc(sizeof(struct data));
Threads
376
10
if(d == NULL) {printf("Konnte keinen Speicher reservieren ...!\n");exit(EXIT_FAILURE);
}
printf("->Haupt-Thread (ID:%ld) gestartet ...\n",pthread_self());
strncpy(d->filename, "testfile", BUF);d->fz = fopen( d->filename, "w+" );if( d->fz == NULL ) {
printf("Konnte Datei %s nicht öffnen\n", d->filename);exit(EXIT_FAILURE);
}
/* Mutex initialisieren */pthread_mutex_init( &d->mutex, NULL );/* Mutex sperren */pthread_mutex_lock( &d->mutex );
/* Threads erzeugen */if(pthread_create (&th1,NULL,&thread_schreiben,d) != 0) {
fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}
/* Threads erzeugen */if (pthread_create (&th2,NULL, &thread_lesen, d) != 0) {
fprintf (stderr, "Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}pthread_join(th1, &ret1);pthread_join(th2, &ret2);
/* Dynamisch angelegten Mutex löschen */pthread_mutex_destroy( &d->mutex );
printf("<-Thread %ld fertig\n", th1);printf("<-Thread %ld fertig\n", th1);printf("<-Haupt-Thread (ID:%ld) fertig ...\n",
pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung kann ich mir hier ersparen, da es exakt dem Beispiel»thread6.c« entspricht, nur dass hierbei eben ein dynamischer Mutex statt eines statischenverwendet wurde.
Threads synchronisieren 10.7
377
Mutex-AttributeMit den folgenden Funktionen können Sie Mutex-Attribute verändern oder abfragen:
#include <pthread.h>
int pthread_mutexattr_init( pthread_mutexattr_t *attr );int pthread_mutexattr_destroy( pthread_mutexattr_t *attr );int pthread_mutexattr_settype( pthread_mutexattr_t *attr,
int kind );int pthread_mutexattr_gettype(
const pthread_mutexattr_t *attr, int *kind );
Mit dem Mutex-Attribut legen Sie fest, was passiert, wenn ein Thread versuchen sollte, einenMutex nochmals zu sperren, obwohl dieser bereits mit pthread_mutex_lock() gesperrtwurde. Mit der Funktion pthread_mutexattr_init() initialisieren Sie zunächst das Mutex-Attributobjekt attr. Zunächst wird hierbei die Standardeinstellung (PTHREAD_MUTEX_FAST_NP)verwendet. Ändern können Sie dieses Attribut mit pthread_mutexattr_settype(). Damitsetzen Sie die Attribute des Mutex-Attributobjekts auf kind. Folgende Konstanten können Siehierbei für kind verwenden:
� PTHREAD_MUTEX_FAST_NP (Standardeinstellung) – pthread_mutex_lock() blockiert den auf-rufenden Thread für immer. Also ein Deadlock.
� PTHREAD_MUTEX_RECURSIVE_NP – pthread_mutex_lock() blockiert nicht und kehrt soforterfolgreich zurück. Wird ein Thread mit diesem Mutex gesperrt, so wird ein Zähler für jedeSperrung um den Wert 1 erhöht. Damit die Sperrung eines rekursiven Mutex aufgehobenwird, muss dieser ebenso oft freigegeben werden, wie er gesperrt wurde.
� PTHREAD_MUTEX_ERRORCHECK_NP – pthread_mutex_lock() kehrt sofort wieder mit demFehlercode EDEADLK zurück, also ähnlich wie mit pthread_mutex_trylock(), nur dass hiereben EBUSY zurückgegeben wird.
10.7.2 Condition-Variablen (Bedingungsvariablen)
Bedingungsvariablen werden dazu verwendet, auf das Eintreffen einer bestimmten Bedin-gung zu warten bzw. die Erfüllung oder den Eintritt einer Bedingung zu zeigen. Bedingungs-variablen werden außerdem mit den Mutexen verknüpft. Dabei wird beim Warten auf eineBedingung eine Sperre zu einem dazu verknüpften Mutex freigegeben (natürlich musstezuvor eine Sperre auf den Mutex erfolgt sein).
Andersherum sollte vor einem Eintreffen auf einer Bedingung eine Sperre auf den verknüpf-ten Mutex erfolgen, so dass nach dem Warten auf diesen Mutex auch die Sperre auf denMutex wieder vorhanden ist. Erfolgte keine Sperre vor dem Signal, wartet ein Thread wieder,bis eine Sperre auf den Mutex möglich ist.
Hinweis
Da die Variablen hierbei mit dem Suffix _NP (non-portable) verbunden sind, sind diese nicht mit demPOSIX-Standard vereinbar und somit nicht geeignet für portable Programme.
Threads
378
10
Statische BedingungsvariablenFür die Bedingungsvariablen wird der Datentyp pthread_cont_t verwendet. Damit eine sol-che Bedingungsvariable überhaupt als statisch definiert ist, muss diese mit der KonstantePTHREAD_COND_INITIALIZER initialisiert werden. Hier die Funktionen, womit Sie mit Condi-tion-Variablen operieren können:
#include <pthread.h>
int pthread_cond_signal( pthread_cond_t *cond );int pthread_cond_broadcast( pthread_cond_t *cond );int pthread_cond_wait( pthread_cond_t *cond,
pthread_mutex_t *mutex );int pthread_cond_timedwait( pthread_cond_t *cond,
pthread_mutex_t *mutex,const struct timespec *abstime);
Bevor Sie zunächst die Funktion pthread_cond_wait() verwenden, müssen Sie beim aufru-fenden Thread das Mutex mutex sperren. Mit einem anschließenden pthread_cond_wait()wird der Mutex dann freigegeben, und der Thread wird mit der Bedingungsvariablen cond,bis zum Eintreffen einer bestimmten Bedingung, blockiert. Bei einem erfolgreichen Aufrufvon pthread_cond_wait() wird auch für den Mutex automatisch die Sperre wieder eingerich-tet – oder einfach, es herrscht wieder der Zustand wie vor dem pthread_cond_wait-Aufruf.
Threads, die auf die Bedingungsvariable cond warten, können Sie mit phtread_cond_signal() wieder aufwecken und weiter ausführen. Bei mehreren Threads, die auf die Bedin-gungsvariable cond warten, bekommt der Thread mit der höchsten Priorität den Zuschlag.
Wollen Sie hingegen alle Threads aufwecken, die auf die Bedingungsvariable cond warten,können Sie die Funktion pthread_cond_signal() verwenden.
Natürlich gibt es auch noch eine Funktion, womit Sie, im Gegensatz zu phtread_cond_wait(),nur eine gewisse Zeit auf die Bedingungsvariable cond warten, bevor sie zum aufrufendenThread zurückkehrt und wieder automatisch die Sperre von Mutex einrichtet –pthread_cond_timewait(). Als Zeit können Sie hierbei abstime verwenden, womit Sie eineabsolute Zeit in Sekunden und Nanosekunden angeben, die seit dem 1.1.1970 vergangensind.
struct timespec {time_t tv_sec; // Sekundenlong tv_nsec; // Nanosekunden
};
Hierzu ein recht einfaches Beispiel, das rein die Funktionalität von Bedingungsvariablen undvor allem deren Verwendung demonstriert.
/* thread9.c */#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>
Threads synchronisieren 10.7
379
#define THREAD_MAX 3
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static void *threads (void *arg) {printf("\t->Thread %ld wartet auf Bedingung\n",
pthread_self());
pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);
printf("\t->Thread %ld hat Bedingung erhalten\n",pthread_self());
printf("\t->Thread %ld: Sende wieder die ""Bedingungsvariable\n", pthread_self());
pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);return NULL;
}
int main (void) {int i;pthread_t th[THREAD_MAX];
printf("->Main-Thread %ld gestartet\n", pthread_self());for(i=0; i<THREAD_MAX; i++)
if (pthread_create (&th[i],NULL, &threads, NULL)!=0) {printf ("Konnte keinen Thread erzeugen\n");exit (EXIT_FAILURE);
}printf("->Main-Thread: habe soeben %d Threads erzeugt\n",
THREAD_MAX);
/* Kurz ruhig legen, damit der Main-Thread als Erstes die* Bedingungsvariable sendet */sleep(1);printf("->Main-Thread: Sende die Bedingungsvariable\n");pthread_cond_signal(&cond);
for(i=0; i<THREAD_MAX; i++)pthread_join (th[i], NULL);
printf("->Main-Thread %ld beendet\n", pthread_self());pthread_exit(NULL);
}
Das Programm bei der Ausführung:
$ gcc -o thread9 thread9.c -lpthread$ ./thread9
Threads
380
10
->Main-Thread -1209416608 gestartet->Main-Thread: habe soeben 3 Threads erzeugt
->Thread -1209418832 wartet auf Bedingung->Thread -1217811536 wartet auf Bedingung->Thread -1226204240 wartet auf Bedingung
->Main-Thread: Sende die Bedingungsvariable->Thread -1209418832 hat Bedingung erhalten->Thread -1209418832: Sende wieder die Bedingungsvariable->Thread -1217811536 hat Bedingung erhalten->Thread -1217811536: Sende wieder die Bedingungsvariable->Thread -1226204240 hat Bedingung erhalten->Thread -1226204240: Sende wieder die Bedingungsvariable
->Main-Thread -1209416608 beendet
Sie sehen hierbei, dass, sobald der Haupt-Thread eine Bedingungsvariable »sendet«, eine Ket-tenreaktion der weiteren Threads entsteht. Hier werden die Threads, entsprechend wie sie inder Queue angelegt wurden, abgearbeitet.
Dazu ein simples Beispiel. In diesem Beispiel wartet der Thread Nummer 2 auf die Condition-Variable von Thread 1. Thread 1 weist einem globalen Zahlenarray werte zehn Werte zu, dieThread 2 anschließend berechnet. Dies ist natürlich auch wieder ein primitives Beispiel undsoll nur die Funktion von Condition-Variablen demonstrieren.
/* thread10.c */#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>
static int werte[10];pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static void thread1 (void *arg) {int ret, i;
printf ("\t->Thread %ld gestartet ...\n",pthread_self ());
sleep (1);ret = pthread_mutex_lock (&mutex);if (ret != 0) {
printf ("Fehler bei lock in Thread:%ld\n",pthread_self());
exit (EXIT_FAILURE);}
/* Kritischer Codeabschnitt */for (i = 0; i < 10; i++)
werte[i] = i;/* Kritischer Codeausschnitt Ende */
Threads synchronisieren 10.7
381
printf ("\t->Thread %ld sendet Bedingungsvariable\n",pthread_self());
pthread_cond_signal (&cond);
ret = pthread_mutex_unlock (&mutex);if (ret != 0) {
printf ("Fehler bei unlock in Thread: %ld\n",pthread_self ());
exit (EXIT_FAILURE);}printf ("\t->Thread %ld ist fertig\n",pthread_self());pthread_exit ((void *) 0);
}
static void thread2 (void *arg) {int i;int summe = 0;
printf ("\t->Thread %ld wartet auf Bedingungsvariable\n",pthread_self ());
pthread_cond_wait (&cond, &mutex);printf ("\t->Thread %ld gestartet ...\n",
pthread_self ());for (i = 0; i < 10; i++)
summe += werte[i];printf ("\t->Thread %ld fertig\n",pthread_self());printf ("Summe aller Zahlen beträgt: %d\n", summe);pthread_exit ((void *) 0);
}
int main (void) {pthread_t th[2];
printf("->Main-Thread %ld gestartet\n", pthread_self());
pthread_create (&th[0], NULL, thread1, NULL);pthread_create (&th[1], NULL, thread2, NULL);
pthread_join (th[0], NULL);pthread_join (th[1], NULL);
printf("->Main-Thread %ld beendet\n", pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread10 thread10.c -lpthread$ ./thread10->Main-Thread -1209416608 gestartet
->Thread -1209418832 gestartet ...
Threads
382
10
->Thread -1217811536 wartet auf Bedingungsvariable->Thread -1209418832 sendet Bedingungsvariable->Thread -1209418832 ist fertig->Thread -1217811536 gestartet ...->Thread -1217811536 fertig
Summe aller Zahlen beträgt: 45->Main-Thread -1209416608 beendet
Dynamische BedingungsvariablenNatürlich steht Ihnen hierzu auch die Möglichkeit zur Verfügung, Bedingungsvariablen dyna-misch anzulegen, wie dies häufig mit Datenstrukturen der Fall ist. Hierzu stehen Ihnen die fol-genden Funktionen zur Verfügung:
#include <pthread.h>
int pthread_cond_init( pthread_cond_t *cond,pthread_condattr_t *cond_attr );
int pthread_cond_destroy( pthread_cond_t *cond );
Mit pthread_cond_init() initialisieren Sie die Bedingungsvariable cond mit den über attrfestgelegten Attributen (hierauf wird im nächsten Abschnitt eingegangen). Verwenden Sie fürattr NULL, werden die standardmäßig voreingestellten Bedingungsvariablen verwendet. Frei-geben können Sie die dynamisch angelegte Bedingungsvariable cond wieder mit der Funktionpthread_cond_destroy().
Hierzu dasselbe Beispiel wie schon im Beispiel »thread10.c« zuvor, nur eben als dynamischeVariante.
/* thread11.c */#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>
struct data {int werte[10];pthread_mutex_t mutex;pthread_cond_t cond;
};
static void thread1 (void *arg) {struct data *d=(struct data *)arg;int ret, i;
Hinweis
In diesem und auch in vielen anderen Beispielen wurde das eine oder andere Mal auf eine Fehlerüber-prüfung verzichtet, was man in der Praxis natürlich tunlichst vermeiden sollte. Allerdings würde ein»perfekt« geschriebenes Programm zu viele Buchseiten in Anspruch nehmen.
Threads synchronisieren 10.7
383
printf ("\t->Thread %ld gestartet ...\n",pthread_self ());
sleep (1);ret = pthread_mutex_lock (&d->mutex);if (ret != 0) {
printf ("Fehler bei lock in Thread:%ld\n",pthread_self());
exit (EXIT_FAILURE);}
/* Kritischer Codeabschnitt */for (i = 0; i < 10; i++)
d->werte[i] = i;/* Kritischer Codeausschnitt Ende */
printf ("\t->Thread %ld sendet Bedingungsvariable\n",pthread_self());
pthread_cond_signal (&d->cond);
ret = pthread_mutex_unlock (&d->mutex);if (ret != 0) {
printf ("Fehler bei unlock in Thread: %ld\n",pthread_self ());
exit (EXIT_FAILURE);}printf ("\t->Thread %ld ist fertig\n", pthread_self());pthread_exit ((void *) 0);
}
static void thread2 (void *arg) {struct data *d=(struct data *)arg;int i;int summe = 0;
printf ("\t->Thread %ld wartet auf Bedingungsvariable\n",pthread_self ());
pthread_cond_wait (&d->cond, &d->mutex);printf ("\t->Thread %ld gestartet ...\n",
pthread_self ());for (i = 0; i < 10; i++)
summe += d->werte[i];printf ("\t->Thread %ld fertig\n",pthread_self());printf ("Summe aller Zahlen beträgt: %d\n", summe);pthread_exit ((void *) 0);
}
int main (void) {pthread_t th[2];struct data *d;
Threads
384
10
/* Speicher für die Struktur reservieren */d = malloc(sizeof(struct data));if(d == NULL) {
printf("Konnte keinen Speicher reservieren ...!\n");exit(EXIT_FAILURE);
}
/* Bedingungsvariablen initialisieren */pthread_cond_init(&d->cond, NULL);
printf("->Main-Thread %ld gestartet\n", pthread_self());
pthread_create (&th[0], NULL, thread1, d);pthread_create (&th[1], NULL, thread2, d);
pthread_join (th[0], NULL);pthread_join (th[1], NULL);
/* Bedingungsvariable freigeben */pthread_cond_destroy(&d->cond);
printf("->Main-Thread %ld beendet\n", pthread_self());return EXIT_SUCCESS;
}
Hierzu noch ein typisches Anwendungsbeispiel. Wir simulieren ein Programm, das Datenempfängt, und erzeugen dabei zwei Threads. Jeder dieser beiden Threads wird mitpthread_cond_wait() in einen Wartezustand geschickt und wartet auf das Signalpthread_cond_signal() vom Haupt-Thread. Ein einfaches Server-Client-Prinzip also. DerHaupt-Thread simuliert dann, er würde zwei Datenpakete an einen Client-Thread verschicken.Der Client-Thread simuliert anschließend, er würde die Datenpakete bearbeiten. Im Beispielwurden statische Bedingungsvariablen verwendet. Die Ausgabe und der Ablauf des Pro-gramms sollten den Sachverhalt außerdem von selbst erklären:
/* thread12.c */#define _MULTI_THREADED#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#define NUMTHREADS 2
static void checkResults (const char *string, int val) {if (val) {
printf ("Fehler mit %d bei %s", val, string);exit (EXIT_FAILURE);
}}
Threads synchronisieren 10.7
385
static pthread_mutex_t dataMutex =PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t DatenVorhandenCondition =PTHREAD_COND_INITIALIZER;
static int DatenVorhanden = 0;static int geteilteDaten = 0;
static void *theThread (void *parm) {int rc;// Datenpaket in zwei Verarbeitungsschrittenint retries = 2;
printf ("\t->Client %ld: gestartet\n", pthread_self ());rc = pthread_mutex_lock (&dataMutex);checkResults ("pthread_mutex_lock()\n", rc);
while (retries--) {while (!DatenVorhanden) {
printf ("\t->Client %ld: Warte auf Daten ...\n",pthread_self ());
rc = pthread_cond_wait ( &DatenVorhandenCondition,&dataMutex);
if (rc) {printf ("Client %ld: pthread_cond_wait()"
" Fehler rc=%d\n", rc, pthread_self ());pthread_mutex_unlock (&dataMutex);exit (EXIT_FAILURE);
}}printf("\t->Client %ld: Daten wurden gemeldet --->\n"
"\t----> Bearbeite die Daten, solange sie ""geschützt sind (lock)\n", pthread_self ());
if (geteilteDaten == 0) {DatenVorhanden = 0;
}}//Ende while(retries--)
printf ("Client %ld: Alles erledigt\n",pthread_self ());
rc = pthread_mutex_unlock (&dataMutex);checkResults ("pthread_mutex_unlock()\n", rc);return NULL;
}
int main (int argc, char **argv) {pthread_t thread[NUMTHREADS];int rc = 0;// Gesamtanzahl der Datenpaketeint anzahlDaten = 4;int i;
Threads
386
10
printf ("->Main-Thread %ld gestartet ...\n");for (i = 0; i < NUMTHREADS; ++i) {
rc=pthread_create (&thread[i], NULL, theThread, NULL);checkResults ("pthread_create()\n", rc);
}
/* Server-Schleife */while (anzahlDaten--) {
sleep (3); // Eine Bremse zum "Mitverfolgen"printf ("->Server: Daten gefunden\n");
/* Schütze geteilte (shared) Daten und Flags */rc = pthread_mutex_lock (&dataMutex);checkResults ("pthread_mutex_lock()\n", rc);printf ("->Server: Sperre die Daten und gib eine "
"Meldung an Consumer\n");++geteilteDaten; /* Füge "shared" Daten hinzu */DatenVorhanden = 1; /* ein vorhandenes Datenpaket *//* Client wieder aufwecken */rc = pthread_cond_signal (&DatenVorhandenCondition);if (rc) {
pthread_mutex_unlock (&dataMutex);printf ("Server: Fehler beim Aufwecken von "
"Client, rc=%d\n", rc);exit (EXIT_FAILURE);
}printf("->Server: Gibt die gesperrten Daten"
" wieder frei\n");rc = pthread_mutex_unlock (&dataMutex);checkResults ("pthread_mutex_lock()\n", rc);
}//Ende while(anzahlDaten--)
for (i = 0; i < NUMTHREADS; ++i) {rc = pthread_join (thread[i], NULL);checkResults ("pthread_join()\n", rc);
}printf ("->Main-Thread ist fertig\n");return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread12 thread12.c -lpthread$ ./thread12->Main-Thread -1073743916 gestartet...
->Client -1209418832: gestartet->Client -1209418832: Warte auf Daten ...->Client -1217811536: gestartet->Client -1217811536: Warte auf Daten ...
->Server: Daten gefunden
Threads synchronisieren 10.7
387
->Server: Sperre die Daten und gib eine Meldung an Consumer->Server: Gibt die gesperrten Daten wieder frei
->Client -1209418832: Daten wurden gemeldet --->----> Bearbeite die Daten, solange sie geschützt sind (lock)->Client -1209418832: Daten wurden gemeldet --->----> Bearbeite die Daten, solange sie geschützt sind (lock)
Client -1209418832: Alles erledigt->Server: Daten gefunden->Server: Sperre die Daten und gib eine Meldung an Consumer->Server: Gibt die gesperrten Daten wieder frei
->Client -1217811536: Daten wurden gemeldet --->----> Bearbeite die Daten, solange sie geschützt sind (lock)->Client -1217811536: Daten wurden gemeldet --->----> Bearbeite die Daten, solange sie geschützt sind (lock)
Client -1217811536: Alles erledigt->Server: Daten gefunden->Server: Sperre die Daten und gib eine Meldung an Consumer->Server: Gibt die gesperrten Daten wieder frei->Server: Daten gefunden->Server: Sperre die Daten und gib eine Meldung an Consumer->Server: Gibt die gesperrten Daten wieder frei->Main-Thread ist fertig
Condition-Variablen-AttributeFür die Attribute von Bedingungsvariablen stehen Ihnen folgende Funktionen zur Verfügung:
#include <pthread.h>
int pthread_condattr_init( pthread_condattr_t *attr );int pthread_condattr_destroy( pthread_condattr_t *attr );
Allerdings machen diese Funktionen noch keinen Sinn, da Linux-Threads noch keine Attri-bute für Bedingungsvariablen anbieten. Diese Funktionen wurden dennoch implementiert,um den POSIX-Standard zu erfüllen.
10.7.3 Semaphore
Threads können auch mit Semaphoren synchronisiert werden. Wie Sie bereits aus dem Kapi-tel zur Interprozesskommunikation erfahren haben, sind Semaphore nichts anderes als nichtnegative Zählvariablen, die man beim Eintritt in einen kritischen Bereich dekrementiert undbeim Verlassen wieder inkrementiert. Hierzu stehen Ihnen folgende Funktionen zur Verfü-gung:
#include <semaphore.h>
int sem_init( sem_t *sem, int pshared, unsigned int value );int sem_wait( sem_t * sem );int sem_trywait( sem_t * sem );int sem_post( sem_t * sem );
Threads
388
10
int sem_getvalue( sem_t * sem, int * sval );int sem_destroy( sem_t * sem );
Alle Funktionen geben bei Erfolg 0 oder bei einem Fehler -1 zurück. Mit der Funktionsem_init() initialisieren Sie das Semaphor sem mit dem Anfangswert value. Geben Sie fürden zweiten Parameter pshared einen Wert ungleich 0 an, kann das Semaphor gemeinsamvon mehreren Prozessen und deren Threads verwendet werden, oder aber, wenn gleich 0 ver-wendet wird, das Semaphor kann nur »lokal« für die Threads des aktuellen Prozesses verwen-det werden.
Die Funktion sem_wait() wird zum Suspendieren eines aufrufenden Threads verwendet.sem_wait() wartet so lange, bis der Zähler sem einen Wert ungleich 0 besitzt. Sobald der Wertvon sem ungleich 0 ist, also z. B. um 1 inkrementiert wurde, kann der suspendierende Threadmit seiner Ausführung fortfahren. Des Weiteren dekrementiert sem_wait, wenn diese Funk-tion »aufgeweckt« wurde, den Zähler des Semaphors wieder um 1. Im Gegensatz zusem_wait() blockiert sem_trywait() nicht, wenn sem gleich 0 ist, und kehrt sofort mit demRückgabewert -1 zurück.
Den Zähler des Semaphors sem können Sie mit der Funktion sem_post() um 1 erhöhen. Wol-len Sie also einen anderen Thread, der mit sem_wait() suspendiert wurde, aufwecken, müs-sen Sie nur sem_post() aus einem anderen Thread aufrufen. sem_post() ist eine nicht blo-ckierende Funktion.
Wollen Sie überprüfen, welchen Wert das Semaphor gerade hat, können Sie die Funktionsem_getvalue() verwenden. Mit der Funktion sem_destroy() löschen Sie das Semaphor semwieder.
Das folgende Beispiel entspricht dem Listing "thread10.c", nur dass hier anstatt Bedingungs-variablen und Mutexen eben ein Semaphor verwendet wird. Mithilfe der Semaphore lässtsich eine Synchronisation (meiner Meinung nach) erheblich einfacher realisieren.
/* thread13.c */#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>#include <semaphore.h>
static int werte[10];sem_t sem;
static void thread1 (void *arg) {int ret, i, val;
printf ("\t->Thread %ld gestartet ...\n",pthread_self ());
/* Kritischer Codeabschnitt */for (i = 0; i < 10; i++)
werte[i] = i;
Threads synchronisieren 10.7
389
/* Kritischer Codeausschnitt Ende */
/* Semaphor um 1 inkrementieren */sem_post(&sem);/* Aktuellen Wert ermitteln */sem_getvalue(&sem, &val);printf("\t->Semaphor inkrementiert (Wert: %d)\n", val);
printf ("\t->Thread %ld ist fertig\n\n",pthread_self());pthread_exit ((void *) 0);
}
static void thread2 (void *arg) {int i;int summe = 0;
/* Semaphor suspendiert, bis der Wert ungleich 0 ist */sem_wait(&sem);
printf ("\t->Thread %ld gestartet ...\n",pthread_self ());
for (i = 0; i < 10; i++)summe += werte[i];
printf ("\t->Summe aller Zahlen beträgt: %d\n", summe);printf ("\t->Thread %ld fertig\n\n",pthread_self());pthread_exit ((void *) 0);
}
int main (void) {pthread_t th[2];int val;
printf("->Main-Thread %ld gestartet\n", pthread_self());/* Semaphor initialisieren */sem_init(&sem, 0, 0);/* Aktuellen Wert abfragen */sem_getvalue(&sem, &val);printf("->Semaphor initialisiert (Wert: %d)\n\n", val);
/* Mit Absicht anders herum */pthread_create (&th[1], NULL, thread2, NULL);pthread_create (&th[0], NULL, thread1, NULL);
pthread_join (th[0], NULL);pthread_join (th[1], NULL);
/* Aktuellen Wert abfragen */sem_getvalue(&sem, &val);printf("->Semaphor (Wert: %d)\n", val);
Threads
390
10
/* Semphor löschen */sem_destroy(&sem);printf("->Semaphor gelöscht\n");printf("->Main-Thread %ld beendet\n", pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread13 thread13.c -lpthread$ ./thread13->Main-Thread -1209416608 gestartet->Semaphor initialisiert (Wert: 0)
->Thread -1217811536 gestartet ...->Thread -1209418832 gestartet ...->Summe aller Zahlen beträgt: 45->Thread -1209418832 fertig
->Semaphor inkrementiert (Wert: 0)->Thread -1217811536 ist fertig
->Semaphor (Wert: 0)->Semaphor gelöscht->Main-Thread -1209416608 beendet
10.7.4 Weitere Synchronisationstechniken im Überblick
Neben den hier vorgestellten Synchronisationsmechanismen bietet Ihnen die phtread-Biblio-thek noch drei weitere an, worauf hier allerdings nur kurz eingegangen werden soll.
� RW-Locks – Mit RW-Locks (Read-Write-Locks) können Sie es einrichten, dass mehrereThreads aus einem (shared) Datenbereich lesen, aber nur ein Thread zum selben Zeitpunktdarin etwas schreiben darf (one-writer, many-reader). Alle Funktionen dazu beginnen mitdem Präfix pthread_rwlock_.
� Barrier – Als Barrier bezeichnet man einen Punkt, der als (unüberwindbare) Barriere ver-wendet wird, die erst überwunden werden kann, wenn eine bestimmte Anzahl vonThreads an diese Barriere kommt, eben das Prinzip der hohen Mauer bei den Pfadpfindern,die man nur im Team (mit einer gewissen Anzahl von Personen) überwinden kann.Solange eine gewisse Anzahl von Threads nicht vorhanden ist, müssen eben alle Threadsvor der Barriere warten. Soll z. B. ein bestimmter Thread erst ausgeführt werden, wennviele andere Threads parallel mehrere Teilaufgaben erledigt haben, sind Barriers eineprima Synchronisationsmöglichkeit. Alle Funktionen zu den Barriers beginnen mit demPräfix pthread_barrier_.
� Spinlocks – Spinlocks sind nur für Multiprozessorsystemen interessant. Das Prinzip ist das-selbe wie bei den Mutexen, nur dass – anders als bei den Mutexen – ein Thread, der aufeinen Spinlock wartet, nicht die CPU freigibt, sondern eine so genannte »busy Loop«(Schleife) ausführt, bis der Spinlock frei ist. Dadurch bleibt ein Kontexwechsel (Contex
Threads abbrechen (canceln) 10.8
391
Switch) erspart. Contex Switch: Beim Kontexwechsel wird der Thread blockiert, und alleInformationen, die für das Weiterlaufen benötigt werden, müssen gespeichert werden. Beivielen Kontexwechseln ist dies ein Menge ersparter Zeit, die man mit Spinlocks gewinnenkann. Alle Funktionen zu den Spinlocks beginnen mit dem Präfix pthread_spin_.
10.8 Threads abbrechen (canceln)
Wird ein Thread abgebrochen bzw. beendet, wurde bisher auch der komplette Thread been-det. Doch auch hierbei ist es möglich, auf eine Abbruchaufforderung zu reagieren. Hierzu sinddrei Möglichkeiten vorhanden:
� PTHREAD_CANCEL_DISABLE – Damit legen Sie fest, dass ein Thread nicht abbrechbar ist. Den-noch bleiben Abbruchaufforderungen von anderen Threads nicht unbeachtet. Diese blei-ben bestehen, und es kann ggf. darauf reagiert werden, wenn man den Thread wieder ineinen abbrechbaren Zustand mittels PTHREAD_CANCEL_ENABLE setzt.
� PTHREAD_CANCEL_DEFERRED – Diese Abbruchmöglichkeit ist die Standardeinstellung beiden Threads. Bei einem Abbruch fährt der Thread so lange fort, bis der nächste Abbruch-punkt erreicht wurde. Man spricht von einem »verzögerten« Abbruchpunkt. Einen solchen»Abbruchpunkt« stellten u.a. Funktionen wie pthread_cond_wait(), pthread_cond_timewait(), pthread_join(), pthread_testcancel(), sem_wait(), sigwait(), open(),close(), read(), write() und noch viele weitere mehr da.
� PTHREAD_CANCEL_ASYNCHRONOUS – Hiermit wird der Thread gleich nach dem Eintreffeneiner Abbruchaufforderung beendet. Hierbei handelt es sich um einen asynchronenAbbruch.
Hierzu die Funktionen, womit Sie einem anderen Thread einen Abbruch senden können undwie Sie die Abbruchmöglichkeiten selbst festlegen.
#include <pthread.h>
int pthread_cancel( pthread_t thread );int pthread_setcancelstate( int state, int *oldstate );int pthread_setcanceltype( int type, int *oldtype );void pthread_testcancel( void );
Mit der Funktion pthread_cancel() schicken Sie dem Thread mit der ID thread einenAbbruchaufforderung. Ob der Thread gleich abbricht oder erst beim nächsten Abbruchpunkt,hängt davon ab, ob hier PTHREAD_CANCEL_DEFERRED (Standard) oder PTHREAD_CANCEL_ASYN-CHRONOUS verwendet wird. Bevor sich der Thread beendet, werden noch, falls verwendet, alleExit-Handler-Funktionen ausgeführt.
Mit der Funktion pthread_setcancelstate() legen Sie fest, ob der Thread auf eine Abbruchauf-forderung reagieren soll (PTHREAD_CANCEL_ENABLE = Default) oder nicht (PTHREAD_CANCEL_DISABLE). Im zweiten Parameter oldstate können Sie den zuvor eingestellten Wert für denThread in der übergebenen Adresse sichern – oder, falls nicht benötigt, NULL angeben.
Threads
392
10
Die Funktion pthread_setcanceltype() hingegen legt über den Parameter type fest, ob derThread verzögert (PTHREAD_CANCEL_DEFERRED = Default) oder asynchron (PTHREAD_CANCEL_ASYNCHRONOUS) beendet werden soll. Auch hier können Sie den alten Zustand des Threads inder Adresse oldtype sichern oder NULL verwenden.
Mit der Funktion pthread_testcancel() können Sie überprüfen, ob eine Abbruchauforde-rung anliegt. Lag eine Abbruchbedingung vor, dann wird der Thread tatsächlich auch beendet.Sie können damit praktisch auch einen eigenen Abbruchpunkt festlegen.
Hier zunächst ein einfaches Beispiel zu pthread_cancel().
/* thread14.c */#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <time.h>
pthread_t t1, t2, t3;static int zufallszahl;
static void cancel_test1 (void) {/* Pseudo-Synchronisation, damit nicht ein Thread
beendet wird, der noch gar nicht läuft. */sleep(1);
if (zufallszahl > 25) {pthread_cancel (t3);printf ("(%d) : Thread %ld beendet %ld\n",
zufallszahl, pthread_self(), t3);printf ("%ld zuende\n", pthread_self());pthread_exit ((void *) 0);
}}
static void cancel_test2 (void) {sleep(1); // Pseudo-Synchronisationif (zufallszahl <= 25) {
pthread_cancel (t2);printf ("(%d) : Thread %ld beendet %ld\n",
zufallszahl, pthread_self(), t2);printf ("%ld zuende\n", pthread_self());pthread_exit ((void *) 0);
}}
static void zufall (void) {srand (time (NULL));zufallszahl = rand () % 50;pthread_exit (NULL);
}
Threads abbrechen (canceln) 10.8
393
int main (void) {if ((pthread_create (&t1, NULL, zufall, NULL)) != 0) {
fprintf (stderr, "Fehler bei ptread_create ...\n");exit (EXIT_FAILURE);
}if((pthread_create(&t2, NULL, cancel_test1, NULL))!=0) {
fprintf (stderr, "Fehler bei ptread_create...\n");exit (EXIT_FAILURE);
}if((pthread_create(&t3, NULL, cancel_test2, NULL))!=0) {
fprintf (stderr, "Fehler bei ptread_create ...\n");exit (EXIT_FAILURE);
}pthread_join (t1, NULL);pthread_join (t2, NULL);pthread_join (t3, NULL);return EXIT_SUCCESS;
}
Hier werden drei Threads erzeugt. Einer der Threads erzeugt eine Zufallszahl, die anderenzwei Threads reagieren entsprechend auf diese Zufallszahl. Je nachdem, ob die Zufallszahlkleiner bzw. größer als 25 ist, beendet der eine Thread den anderen mit pthread_cancel().Wenn Sie das Programm ausführen, wird trotzdem, nach Beendigung einer der beidenThreads mit pthread_cancel(), zweimal ausgegeben:
Thread n beendet
Wie kann das sein, wo Sie doch mindestens einen Thread beendet haben? Das ist die zweiteBedingung zur Beendigung von Threads, nämlich die Reaktion auf die Abbruchanforderun-gen. Die Standardeinstellung lautet hier ja PTHREAD_CANCEL_DEFERRED. Damit läuft der Threadnoch bis zum nächsten Abbruchpunkt, in unserem Fall pthread_exit(). Wenn Sie einenThread sofort abbrechen wollen bzw. müssen, müssen Sie mit pthread_setcanceltype() dieKonstante PTHREAD_CANCEL_ASYNCHRONOUS setzen, z. B. in der main-Funktion mit:
if ((pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL))!= 0) {
fprintf(stderr, "Fehler bei pthread_setcanceltype\n");exit (EXIT_FAILURE);
}
In der Praxis kann man aber von asynchronen Abbrüchen abraten, da diese an jeder Stelle auf-treten können. Wird z. B. pthread_mutex_lock() aufgerufen und tritt hier der Abbruch ein,nachdem das Mutex gesperrt wurde, hat man schnell einen Deadlock erzeugt. Einen asyn-chronen Abbruch sollte man in der Praxis nur verwenden, wenn die Funktion »asynchronsi-cher« ist, was mit pthread_cancel(), pthread_setcancelstate() und pthread_setcancel-type() nicht allzu viele Funktionen sind. Wenn Sie schon asynchrone Abbrüche verwendenmüssen, dann eben immer wenn ein Thread keine wichtigen Ressourcen beinhaltet (wiereservierter Speicherplatz (Memory Leaks), Sperren etc.).
Ein besonders häufiger Anwendungsfall von PTHREAD_CANCEL_DISABLE sind kritischeCodebereiche, die auf keinen Fall abgebrochen werden dürfen. Zum Beispiel ist dies sinnvoll
Threads
394
10
bei wichtigen Einträgen in Datenbanken, bei komplexen Maschinensteuerungen. Am bestenrealisiert man solche Codebereiche, indem man den kritischen Abschnitt als unabbrechbareinrichtet und gleich danach den alten Zustand wiederherstellt.
int oldstate;
/* Thread als unabbrechbar einrichten */if ((pthread_setcancelstate( PTHREAD_CANCEL_DISABLE,
&oldstate))!= 0) {fprintf(stderr, "Fehler bei pthread_setcancelstate\n");exit (EXIT_FAILURE);
}
/* ----------------------------------------- *//* Hier kommt der kritische Codebereich rein *//* ----------------------------------------- */
/* Alten Zustand des Threads wieder herstellen */if ((pthread_setcancelstate(oldstat, NULL))!= 0) {
fprintf(stderr, "Fehler bei pthread_setcancelstate\n");exit (EXIT_FAILURE);
}
Ein einfaches Beispiel hierzu:
/* thread15.c */#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <time.h>
static void cancel_test (void) {int oldstate;
/* Thread als unabbrechbar einrichten */if ((pthread_setcancelstate( PTHREAD_CANCEL_DISABLE,
&oldstate))!= 0) {printf("Fehler bei pthread_setcancelstate\n");exit (EXIT_FAILURE);
}
printf("Thread %ld im kritischen Codeabschnitt\n",pthread_self());
sleep(5); // 5 Sekunden warten
/* Alten Zustand des Threads wieder herstellen */if ((pthread_setcancelstate(oldstate, NULL))!= 0) {
printf("Fehler bei pthread_setcancelstate\n");exit (EXIT_FAILURE);
}
Erzeugen von Thread-spezifischen Daten (TSD-Data) 10.9
395
printf("Thread %ld nach dem kritischen Codeabschnitt\n",pthread_self());
pthread_exit ((void *) 0);}
int main (void) {pthread_t t1;int *abbruch;
printf("Main-Thread %ld gestartet\n", pthread_self());
if((pthread_create(&t1, NULL, cancel_test, NULL)) != 0) {fprintf (stderr, "Fehler bei pthread_create ...\n");exit (EXIT_FAILURE);
}/* Abbruchaufforderung an den Thread */pthread_cancel(t1);pthread_join (t1, &abbruch);if( abbruch == PTHREAD_CANCELED )
printf("Thread %ld wurde abgebrochen\n", t1);printf("Main-Thread %ld beendet\n", pthread_self());return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread15 thread15.c -lpthread$ ./thread15Main-Thread -1209416608 gestartetThread -1209418832 im kritischen CodeabschnittThread -1209418832 nach dem kritischen CodeabschnittThread -1209418832 wird abgebrochenMain-Thread -1209416608 beendet
Ohne das Setzen von PTHREAD_CANCEL_DISABLE am Anfang des Threads »cancel_test« würdedas Beispiel keine fünf Sekunden mehr warten und auch nicht mehr ausgeben »Thread –1209418832 nach dem kritischen Codeabschnitt« – am besten testen Sie dies, indem Sie dasVerändern des Cancel-Status auskommentieren oder eben anstatt PTHREAD_CANCEL_DISABLEdie Konstante PTHREAD_CANCEL_ENABLE verwenden.
10.9 Erzeugen von Thread-spezifischen Daten (TSD-Data)
Bei einem Aufruf von Funktionen werden die lokalen Daten auf dem Stack abgelegt und auchwieder abgeholt. Bei den Threads kann man ja solch langlebige Daten entweder mit zusätzli-chen Argumenten an die einzelnen Threads weitergeben oder aber in globalen Variablen spei-chern. Wenn Sie aber z. B. vorhaben, eine Bibliothek für den Multithread-Gebrauch zu schrei-ben, ist dies nicht mehr möglich, da man ja hierbei die Argumentzahl nicht mehr verändernkann, damit auch ältere Programme ohne Threads diese Bibliothek verwenden können und
Threads
396
10
man auch nicht weiß, wie viele Threads die Bibliotheksfunktionen nutzen werden, kann mannun mal keine Aussage darüber machen, wie groß die globalen Daten sein sollen.
Das Ganze vielleicht noch etwas vereinfacht erklärt, es ist einfach nicht möglich, dass globaleund statische Variablen unterschiedliche Werte in den verschiedenen Threads haben können.Aus diesem Grund wurden »Schlüssel« eingeführt – oder auch Thread-spezifische Daten (kurzTSD-Data). Dabei handelt es sich um eine Art Zeiger, der immer auf die Daten verweist, diedem Thread gehören, der eben einen entsprechenden »Schlüssel« benutzt. Beachten Sie aller-dings, dass hierbei immer mehrere Threads den gleichen »Schlüssel« benutzen – nicht jederThread einen extra »Schlüssel«!
Dabei bekommt jeder Thread einen privaten Speicherbereich mit einem eigenen Schlüsselzugeteilt. Dies kann man sich gerne als ein Array von void-Zeigern vorstellen, auf die derThread mit »seinem« Schlüssel zugreifen kann.
Hierzu die Funktionen, womit Sie Thread-spezifische Daten erzeugen bzw. die damit arbeitenkönnen.
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*) );
int pthread_key_delete( pthread_key_t key );int pthread_setspecific(
pthread_key_t key, const void *pointer );void * pthread_getspecific(pthread_key_t key);
Mit pthread_key_create() erzeugen Sie einen neuen TSD-Schlüssel mit der Speicherstellekey des Schlüssels und (als zweites Argument) entweder NULL oder die Funktion, um den Spei-cher der Daten wieder freizugeben, eine Exit-Handler-Funktion, wenn Sie so wollen.
Mit der Funktion pthread_setspecific() können Sie Daten mit dem TSD-Schlüssel assoziie-ren. Sie legen damit praktisch die TSD-Daten für den TSD-Schlüssel key über den Zeiger poin-ter fest.
Mit der Funktion pthread_getspecific() kann man die Daten aus dem TSD-Schlüssel ausle-sen.
Mit dem folgenden Beispiel werden MAX_THREADS Threads erzeugt, von denen jeder Threadeine Thread-eigene Datei mittels TDS-Daten erzeugt. Im Beispiel wird hierbei nur protokol-liert, dass der Thread gestartet und wieder beendet wurde. Zwischen den beiden Zeilen soll-ten Sie die eigentliche Arbeit des Threads eintragen. Fehler oder sonstige Meldungen dieserArbeit können Sie ebenfalls wieder mit »thread_write« in die für den Thread vorgeseheneDatei schreiben. Dass diese Funktion »nur« mit einem einfachen String aufgerufen werdenkann, ist dem TSD-Schlüssel zu verdanken, der in der Funktion »thread_write« mittelspthread_getspecific() eingelesen wird. Ein simples Grundgerüst eben, womit Sie ohne gro-ßen Aufwand Thread-eigene Logdateien verwenden können.
/* thread17.c */#include <pthread.h>#include <stdio.h>
Erzeugen von Thread-spezifischen Daten (TSD-Data) 10.9
397
#include <stdlib.h>#define MAX_THREADS 3
/* TSD-Datenschlüssel */static pthread_key_t tsd_key;
/* Schreibt einen Text in eine Datei für* jeden aktuellen Thread */void thread_write (const char* text) {/* TSD-Daten lesen */FILE* th_fp = (FILE*) pthread_getspecific (tsd_key);fprintf (th_fp, "%s\n", text);
}
/* Am Ende den Zeiger auf die Datei(en) schließen */void thread_close (void* th_fp) {fclose ((FILE*) th_fp);
}
void* thread_tsd (void* args) {char th_fpname[20];FILE* th_fp;
/* Einen Thread-spezifischen Dateinamen erzeugen */sprintf(th_fpname,"thread%d.thread",(int) pthread_self());/* Die Datei öffnen */th_fp = fopen (th_fpname, "w");if( th_fp == NULL )pthread_exit(NULL);
/* TSD-Daten zu TSD-Schlüssel festlegen */pthread_setspecific (tsd_key, th_fp);
thread_write ("Thread wurde gestartet ...\n");
/* Hier kommt die eigentliche Arbeit des Threads hin */
thread_write("Thread ist fertig ...\n");pthread_exit(NULL);
}
int main (void) {int i;pthread_t threads[MAX_THREADS];
/* Einen neuen TSD-Schlüssel erzeugen - Beim Ende eines* Threads wird die Funktion thread_close ausgeführt */
pthread_key_create (&tsd_key, thread_close);/* Threads erzeugen */for (i = 0; i < MAX_THREADS; ++i)pthread_create (&(threads[i]), NULL, thread_tsd, NULL);
Threads
398
10
/* Auf die Threads warten */for (i = 0; i < MAX_THREADS; ++i)pthread_join (threads[i], NULL);
return EXIT_SUCCESS;}
Das Programm bei der Ausführung:
$ gcc -o thread17 thread17.c -lpthread$ ./thread17$ ls *.threadthread-1209418832.thread thread-1217811536.thread thread-1226204240.thread$ cat thread-1209418832.threadThread wurde gestartet ...
Thread ist fertig ...
10.10 pthread_once – Codeabschnitt einmal ausführen
In den Beispielen bisher haben Sie häufig mehrere Threads gestartet. Manchmal tritt aber eineSituation ein, wo man gewisse Vorbereitungen treffen muss – z. B. eine bestimmte Datei anle-gen, weil mehrere Threads darauf zugreifen müssen oder man eine Bibliotheksroutine entwi-ckelt. Ist es nicht möglich, solche Vorbereitungen beim Start des Haupt-Threads zu treffen,sondern muss man dies in einem der Neben-Threads machen, benötigt man einen Mechanis-mus, wo eine Funktion exakt nur einmal ausgeführt wird, egal wie oft und von welchemThread aus diese aufgerufen wurde. Ein einfaches Beispiel, worauf ich hinauswill:
/* thread18.c */#include <pthread.h>#include <stdio.h>#include <stdlib.h>#define MAX_THREADS 3
void thread_once(void) {printf("Funktion thread_once() aufgerufen\n");
}
void* thread_func (void* args) {printf("Thread %ld wurde gestartet\n", pthread_self());thread_once();printf("Thread %ld ist fertig gestartet\n",
pthread_self());pthread_exit(NULL);
}
int main (void) {int i;pthread_t threads[MAX_THREADS];
pthread_once – Codeabschnitt einmal ausführen 10.10
399
/* Threads erzeugen */for (i = 0; i < MAX_THREADS; ++i)pthread_create (&(threads[i]), NULL, thread_func, NULL);
/* Auf die Threads warten */for (i = 0; i < MAX_THREADS; ++i)pthread_join (threads[i], NULL);
return EXIT_SUCCESS;}
Das Programm bei der Ausführung:
$ gcc -o thread18 thread18.c -lpthread$ ./thread18Thread -1209418832 wurde gestartetFunktion thread_once() aufgerufenThread -1209418832 ist fertig gestartetThread -1217811536 wurde gestartetFunktion thread_once() aufgerufenThread -1217811536 ist fertig gestartetThread -1226204240 wurde gestartetFunktion thread_once() aufgerufenThread -1226204240 ist fertig gestartet
Beabsichtigt war in diesem Beispiel, dass die Funktion »thread_once« nur einmal aufgerufenwird. Aber wie das für Thread-Programme eben üblich ist, wird die Funktion bei jedemThread aufgerufen. Hier kann man zwar mit den Thread-spezifischen Synchronisationen nach-helfen, aber die Thread-Bibliothek bietet Ihnen hier mit der Funktion pthread_once() eineFunktion an, die einen bestimmten Code nur ein einziges Mal ausführt.
#include <pthread.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
Vor der Ausführung von pthread_once() muss man eine statische Variable mit der KonstantePTHREAD_ONCE_INIT initialisieren, bevor man diese an pthread_once() (erster Parameter)übergibt. Als zweites Argument übergeben Sie pthread_once(), die Funktion, die den einmalauszuführenden Code enthält. Wird dieser einmal auszuführende Code ausgeführt, wird diesin der Variablen once_control vermerkt, so dass diese Funktion kein zweites Mal mehr aus-geführt werden kann. Hierzu das Beispiel »thread18.c« nochmals mit phtread_once():
/* thread19.c */#include <pthread.h>#include <stdio.h>#include <stdlib.h>#define MAX_THREADS 3
static pthread_once_t once = PTHREAD_ONCE_INIT;
void thread_once(void) {printf("Funktion thread_once() aufgerufen\n");
Threads
400
10
}
void* thread_func (void* args) {printf("Thread %ld wurde gestartet\n", pthread_self());pthread_once(&once, thread_once);printf("Thread %ld ist fertig gestartet\n",
pthread_self());pthread_exit(NULL);
}
int main (void) {int i;pthread_t threads[MAX_THREADS];/* Threads erzeugen */for (i = 0; i < MAX_THREADS; ++i)pthread_create (&(threads[i]), NULL, thread_func, NULL);
/* Auf die Threads warten */for (i = 0; i < MAX_THREADS; ++i)pthread_join (threads[i], NULL);
return EXIT_SUCCESS;}
Das Programm bei der Ausführung:
$ gcc -o thread19 thread19.c -lpthread$ ./thread19Thread -1209418832 wurde gestartetFunktion thread_once() aufgerufenThread -1209418832 ist fertig gestartetThread -1217811536 wurde gestartetThread -1217811536 ist fertig gestartetThread -1226204240 wurde gestartetThread -1226204240 ist fertig gestartet
Jetzt wird die mit pthread_once() eingerichtete Funktion tatsächlich nur noch einmal ausge-führt.
10.11 Thread-safe (thread-sichere Funktionen)
Die Thread-Programmierung kann nur mit Bibliotheken realisiert werden, die als thread-sicher (thread-safe) gelten. Denn auch die Bibliotheken müssen die parallele Ausführungerlauben, um nicht ins Straucheln zu geraten. Mit der Einführung von Glibc 2.0 wurden dieLinux-Threads in den Bibliotheken implementiert und müssen nicht extra besorgt werden.Somit ist strcmp() – auch wenn schon über 15 Jahre alt – thread-safe.
readdir() hingegen ist z. B. nicht thread-safe. Das Problem mit readdir() ist, wenn mehrereThreads denselben DIR-Zeiger verwenden, dass sie immer den aktuellen Rückgabewert vomzuvor erhaltenen Thread überschreiben. Als Alternative für Funktionen, die nicht als thread-safe gelten (auch wenn es nicht sehr viele sind), wurden thread-sichere Schnittstellen, die in
Threads und Signale 10.12
401
der Regel mit der Endung _r (reentrante-Funktionen) gekennzeichnet wurden, eingeführt.Die thread-sichere Alternative zu readdir() lautet somit readdir_r().
Die Endung _r zeigt Ihnen außerdem nicht nur, dass die Funktion thread-sicher ist, sondernauch, dass diese Funktion keinen internen statischen Puffer verwendet (der beim Aufruf der-selben Funktion in mehreren Threads jedes Mal überschrieben wird). Hierzu eine kurze Listezu einigen gängigen Reentrante-Funktionen, deren genauere Syntax und Verwendung Siebitte der entsprechenden Man-Page entnehmen.
10.12 Threads und Signale
Signale lassen sich auch mit den Threads realisieren, nur muss man hierbei Folgendes beach-ten:
� Signale, die von der Hardware gesendet werden, bekommt immer der Thread, der dasHardware-Signal gesendet hat.
� Jedem Thread kann eine eigene Signalmaske zugeordnet werden. Allerdings gelten Sig-nale, 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 );
nicht thread-sicher thread-sicher Bedeutung
getlogin getlogin_r Loginname ermitteln
ttyname ttyname_r Terminalpfadname ermitteln
readdir readdir_r Verzeichniseinträge lesen
strtok strtok_r String anhand Tokens zerlegen
asctime asctime_r Zeitfunktion
ctime ctime_r Zeitfunktion
gmtime gmtime_r Zeitfunktion
localtime localtime_r Zeitfunktion
rand rand_r (Pseudo-)Zufallszahlen generieren
getpwuid getpwuid_r Eintrag in /etc/passwd erfragen (via UID)
getpwnam getpwnam_r Eintrag in /etc/passwd erfragen (via Loginname)
getgrgid getgrgid_r Eintrag in /etc/group erfragen (via GID)
getgrnam getgrnam_r Eintrag in /etc/group erfragen (via Gruppenname)
Tabelle 10.1 Nicht thread-sichere Funktionen und die Alternativen
Threads
402
10
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 Threadsund nicht Prozesse bezogen. Abgesehen von den Signalen SIGKILL und SIGSTOP können Sieauch 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 zweitesArgument ist ein Zeiger auf einen Satz von Signalen nötig, der die aktuelle Signalmaskeergänzt, sie entfernt oder die Signalmaske ganz übernimmt. Hierfür kann auch NULL angege-ben werden. Der dritte Parameter ist ein Zeiger auf die aktuelle Signalmaske. Hiermit könnenSie entweder die aktuelle Signalmaske abfragen oder, wenn Sie mit dem zweiten Parametereine neue Signalmaske einrichten, die alte Signalmaske sichern. Aber auch der dritte Parame-ter kann NULL sein. Wenn ein Thread einen weiteren Thread erzeugt, erbt dieser ebenfalls dieSignalmaske. Wollen Sie also, dass alle Threads diese Signalmaske erben, sollten Sie vor derErzeugung der Threads im Haupt-Thread die Signalmaske setzen.
Mit der Funktion pthread_kill() senden Sie dem Thread thread das Signal signo. Hierbeisollte man noch die Besonderheiten mit den Signalen SIGKILL, SIGTERM und SIGSTOP erläu-tern. 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 derThread. Ebenso sieht dies mit dem Signal SIGSTOP aus – hier wird der ganze Prozess (mit allenlaufenden Threads) angehalten, bis ein anderer Prozess (nicht Thread) SIGCONT an den ange-haltenen Prozess sendet.
Mit sigwait() halten Sie einen Thread so lange an, bis eines der Signale aus der Menge setgesendet wird. Die Signalnummer wird noch in sig geschrieben, bevor der Thread seine Aus-führung fortsetzt. Wurde dem Signal ein Signalhandler zugeteilt, wird nichts in sig geschrie-ben.
Hierzu ein einfaches Beispiel, das Ihnen die Verwendung von Signalen in Verbindung mitThreads 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());
}
Threads und Signale 10.12
403
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());
Threads
404
10
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);
}
Zusammenfassung und Ausblick 10.13
405
Das Programm bei der Ausführung:
$ gcc -o thread20 thread20.c -lpthread$ ./thread20TID(-1209418832) erzeugtTID(-1217815632) erzeugtHaupt-Thread(-1209416608) sendet SIGINT an TID(-1209418832)Haupt-Thread(-1209416608) sendet SIGUSR1 an TID(-1209418832)Haupt-Thread(-1209416608) wird beendetSIGUSR1 erhalten von TID(-1209418832)SIGINT erhalten von TID(-1209418832)TID(-1209418832) sende SIGINT an -1217815632TID(-1209418832) sende SIGUSR1 an -1217815632SIGUSR1 erhalten von TID(-1217815632)TID(-1217815632) wird beendetTID(-1217815632) Exit-Status = 99TID(-1209418832) wird beendet
Dieses Beispiel beinhaltet drei Threads (inklusive dem Haupt-Thread). Im Haupt-Thread wirddie Signalmaske eingerichtet, so dass alle Signale im Haupt-Thread blockiert (SIGBLOCK) wer-den. 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 ande-ren Threads abgehängt. Dann werden im Haupt-Thread die Signale SIGINT und SIGUSR1 anden »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 SIGUSR1vom 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 denExit-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 werdenja 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 derThread mit einem Rückgabewert beendet (auf den »thread_1« ja mit pthread_join() wartet).
10.13 Zusammenfassung und Ausblick
Thread ist sicherlich ein ziemliches Schlagwort in den letzten Jahren geworden, und viele Pro-grammierer sind in zwei Lager gespalten – in die Befürworter und die Gegner von Threads.Zugegeben, Threads haben je nach Anwendungsfall auch ihre Macken. Gerade in der Netz-werkprogrammierung sorgen Threads bezüglich der Skalierbarkeit immer wieder für Diskus-sionsstoff. Das Problem besteht darin, dass es bei Threads häufig ein hartes Limit der gleich-zeitigen Threads gibt.
Threads
406
10
Wie dem auch sei, trotzdem lassen sich die Hersteller von namhafter Software wie den Server-anwendungen BIND, Apache etc. nicht aufhalten, Thread-Unterstützung in ihre Software ein-zubauen.
Ob Sie nun Threads in Ihre Software einbauen wollen oder nicht, müssen Sie letztendlichselbst entscheiden und hängt größtenteils auch von der Art der Software ab. Am besten wärees sicherlich, wenn Sie Ihre Software mit Thread-Unterstützung anbieten – sprich, der Anwen-der kann diese bei Bedarf aktivieren bzw. testen.
Mit diesem Kapitel haben Sie auf jeden Fall ein fundiertes Polster an Wissen, so dass Sie Soft-ware mit Thread-Unterstützung schreiben können. Aber wie auch bei fast jedem Thema einesBuches gibt es immer noch einiges mehr, das man zu den Threads noch hätte schreiben kön-nen. Aber immerhin, im Gegensatz zur ersten Auflage des Buches wurde der Umfang desKapitels hier verdoppelt.
1225
Index
$! 996$# 996, 1001$$ 996$* 996$? 996$@ 996$0 996$1 996$1, $2, ... $9 1000& (Hintergrundprozess) 956* (Shell) 972/dev-Verzeichnis 149/etc/group 179
Struktur 180/etc/gshadow 179/etc/hosts 439/etc/inittab 243/etc/ld.so.conf 884/etc/passwd 169/etc/resolv.conf 439/etc/services 443/etc/shadow 169, 174
Struktur 175/etc/shells 170/etc/skel 184/etc/syslog.conf 236/proc
/proc/$pid/cmdline 136/proc/$pid/cwd 139/proc/$pid/environ 137/proc/$pid/exe 139/proc/$pid/fd/ 138/proc/$pid/maps 139/proc/$pid/root 139/proc/$pid/stat 139/proc/$pid/statm 139/proc/$pid/status 139/proc/apm 133/proc/cpuinfo 131/proc/devices 132/proc/dma 132/proc/filesystems 146/proc/ide/ 133/proc/interrupts 132/proc/ioports 133/proc/loadavg 133/proc/locks 145/proc/meminfo 129, 132
/proc (Forts.)/proc/modules 145/proc/mounts 133, 146/proc/mtab 146/proc/net/ 133/proc/scsi 133/proc/scsi/scsi 133/proc/self 137/proc/stat 133/proc/sys/dev/cdrom/autoeject
140/proc/sys/kernel/ctrl-alt-del 140/proc/sys/kernel/osrelease 140/proc/sys/kernel/ostype 140/proc/sys/kernel/version 140/proc/uptime 133/proc/version 129Filesystem 146Hardware-/Systeminformatio-
nen 131Informationen abfragen 129Kernelinformationen 139Prozessinformationen 135
/proc-Verzeichnis 129/usr/bin 859/usr/local/bin 859<< (Shell) 1033<arpa/inet.h> 1139<assert.h> 1088<complex.h> 1107<conio.h> 680<ctype.h> 1088<curses.h> 673, 679<dirent.h> 99, 1121<dlfcn.h> 886<errno.h> 47, 112, 1089<fenv.h> 1107<float.h> 1090<glib.h> 720<inttypes.h> 1109<iso646.h> 1105<libpq-fe.h> 1146<limits.h> 47, 1091<limits.h> (POSIX) 1112<locale.h> 1091<math.h> 1094<math.h> C99 1110<netdb.h> 1140
<netinet/in.h> 433, 1138<pthread.h> 1132<regex.h> 106<setjmp.h> 1095<signal.h> 1096<stdarg.h> 87, 1096<stdbool.h> 1109<stddef.h> 1097<stdint.h> 1109<stdio.h> 84, 1098<stdlib.h> 1100<string.h> 1102<sys/mman.h> 1116<sys/socket.h> 1136<sys/stat.h> 115, 1122<term.h> 673<termios.h> 654<tgmath.h> 1112<time.h> 126, 923, 1104<unistd.h> 1113<wchar.h> 1106<wctype.h> 1106? (Shell) 973[ (Kommando) 1012_Exit 1102_Exit() 1102_IOFBF 91_IOLBF 91_IONBF 91_POSIX_CHOWN_RESTRICTED
1113_POSIX_JOB_CONTROL 1113_POSIX_NO_TRUNC 1113_POSIX_PRIORITY_SCHEDULING
194_POSIX_SAVED_IDS 1113_POSIX_SOURCE 1113_POSIX_THREAD_PRIORITY_
SCHEDULING 364_POSIX_VERSION 1113_XOPEN_VERSION 1113~ (Shell) 9750_EXCL 93
A
Abdeckungsanalyse 927abort() 1101
Index
1226
abs() 1101accept() 421, 1136access() 1124acos() 1094ACS_-Sonderzeichen 686addch() 687addnstr() 689addstr() 689Advisory Locking 68AF_APPLETALK 417AF_ATMPCV 417AF_AX25 417AF_INET 416AF_INET6 416AF_INIX 416AF_IPX 416AF_LOCAL 416AF_NETLINK 416AF_PACKET 417AF_UNIX 416AF_X25 417afio 1190AIX 30–31alarm() 268, 1129alias 1221Allegro 853alphasort() 104ANSI C 37, 1087ANSI C99 1105ANSI-Steuersequenzen 672apropos 1219ar 881ARG_MAX 1113arp 1206arpa 1139Array
Shell 989AS 860asctime() 1104ash 954A-Shell 954asin() 1094asnprintf() 87asprintf() 87Assembler 860assert 1088at 1173atan() 1094atan2() 1094atexit() 742, 1101atof() 1100atoi() 1100
atol() 1100atoll() 1102Atomare Operation 265Atomare Variable 265attroff() 696attron() 696attrset() 696Ausführrechte 1071Ausgaben verknüpfen
Shell 967AUTOCONF 949AUTOMAKE 949awk 988
B
Back Quotes 998badblocks 1180basename 1168Bash 954batch 1173bc 1221
wissenschaftliches Rechnen 982Benutzerdatenbank 169bg 250, 1174
Shell 1060Bibliotheken 880
Dynamisch nachladen 886Dynamische 883Erstellen 880Shared Libraries 883Statische 880
Big-Endian 425Bildschirm ab Cursor löschen 690Bildschirm löschen 677, 690Binäres Lesen und Schreiben 88bind() 420, 1136
Verwenden oder weglassen 496Blockgröße 55, 124Bourne-Shell 953box() 701Brace Extension
Shell 974BSD 30–31
FreeBSD 30–31NetBSD 31OpenBSD 31
bsearch() 1101btowc() 1089Buffer Overflow 89, 942, 1071bunzip2 1190
bzcat 1151bzip2 1190
C
C99 1105cal 1202calloc() 1101can_change_colors() 697case
Shell 1020cat 1151cbreak() 681cd 1168ceil() 1094cfdisk 1181cfgetispeed() 667cfgetospeed() 667cfmakeraw() 665cfsetispeed() 669cfsetospeed() 669CHAR_BIT 1091CHAR_MAX 1091CHAR_MIN 1091chdir() 95, 1121chgrp 1152CHILD_MAX 1113chmod 1153chmod() 82, 117, 1124chown 1154chown() 1124chroot() 1075chtype 687cksum 1152clear 1217clear() 690clearenv() 206, 1125clearerr() 90, 1100clearok() 691clock() 1104close() 52, 423, 1114closedir() 100, 103, 1121closelog() 236Close-on-exec Flag 62close-on-exec-Flag 59, 291clrtobot() 690clrtoeol() 690cmp 1154COLOR_PAIR() 698comm 1154Compiler
GCC 33, 859
Index
1227
complex 1107compress 1190Condition-Variablen 377conio 680connect() 418, 1136Copy on write-Verfahren 212copysign() 1110Core Dump 1082cos() 1094cosh() 1094cp 1155cpio 1190creat() 51cron 1174cron-Job 252crypt 1193csh 953C-Shell 953
Erweiterung 955csplit 1156ctermid() 665ctime() 1104ctype 1088curses 673, 679curses-Bibliothek 679Cursor positionieren 677, 686Cursortasten 684cut 1156
Shell 984CVS 898, 908
Arbeitskopie auschecken 911Archiv einrichten 909Datei entfernen 921Datei hinzufügen 920Datei umbennenen 921Grafische Frontends 922Konflikte 915Neues Projekt einrichten 910Remote-Zugriff 922Repository 909Veränderungen einbringen 913Veränderungen rückgänig
machen 919Veränderungen untersuchen 913Veränderungen weitergeben 914Verzeichnis entfernen 922Verzeichnis hinzufügen 920
D
Dämoncron 232
Dämon (Forts.)div. Netzwerkdämons 233Drucker (lpd, cupsd) 232Erzeugen 238klogd 233syslogd 233xinetd 232
Dämonprozess 232dash 954date 1202Datei
Beschneiden 81Binäres Lesen 88Binäres Schreiben 88Dateiart ermitteln 116Eigentümer 119Größe ermitteln 124Gruppeneigentümer 119Lesen 55Lesen mehrerer Puffer 79Link 120Löschen 92Neu anlegen 48, 51Öffnen 46, 85Positionieren 57, 90Schließen 52, 85Schreiben 52Schreiben mehrerer Puffer 79Sperren 66Status ermitteln (Shell) 1015Temporäre- 1074Temporäre Dateien erstellen 93Umbenennen 92Zeitstempel (Inode-Änderung)
125Zeitstempel (Lesezugriff) 125Zeitstempel (Schreibzugriff) 125Zugriffsrechte 48, 1069Zugriffsrechte erfragen 116Zugriffsrechte verändern 82
Dateisperren 281Dateitabelleneintrag 46Datenbankprogrammierung 519Datenbankserver 521Datenbanksystem
ADABAS-D 522Attribut und Attributname 520Beziehungstypen 520Domäne 520Entität 520Grad der Relation 520Informix 522
Datenbanksystem (Forts.)Kardinalität 520Msql 522Normalisierungstheorie 521Oracle 522Relation 520Relationales 519SQL 521SQL-Server 522Sybase 522Transaktionen 522Tupel 520
DBL_DIG 1090DBL_EPSILON 1091DBL_MANT_DIG 1090DBL_MAX 1091DBL_MAX_10_EXP 1090DBL_MAX_EXP 1090DBL_MIN 1091DBL_MIN_10_EXP 1090DBL_MIN_EXP 1090dd 1181dd_rescue 1183DDD 930Deadlocks 282Debuggen 865, 929
DDD 930GDB 930
delch() 690delwin() 701dev 149Devices 149Dezimalzahl
Shell 979Dezimalzahlen vergleichen
Shell 1012df 1179DIFF 949diff 1156diff3 1157difftime() 1104dig 1210DIR 100dircmp 1168dirent 99, 1121dirent-Struktur 1121dirfd() 100dirname 1168div() 1101dlclose() 886dlerror() 886dlopen() 886
Index
1228
dlsym() 886dmesg 1203dos2unix 1157DoS-Attacken 209Double Quotings 997DOXYGEN 949Druckerbefehle 1205DT_BLK 98DT_CHR 98DT_DIR 98DT_FIFO 98DT_REG 98DT_SOCK 98DT_UNKNOWN 98DTTOIF() 99du 1179dump 1193dumpe2fs 1183dup() 59, 291, 1114dup2() 59, 291, 1114dupwin() 701
E
E2BIG 1090e2fsck 1184EACCES 1090EBADF 1090echo
Shell 1028echo() 682ECONTR 1089ECURDIR 1089EDOM 1089EEXIST 1090EFAULT 1090efence 942EGID 189Eingabe
Shell 1031Einschränkungsmaske 50
Setzen und Abfragen 50EINVACC 1089EINVAL 1090EINVDAT 1089EINVDRV 1089EINVENV 1089EINVFMT 1089EINVFNC 1089EINVMEM 1089Elementare E/A-Funktionen 43
elifShell 1010
elseShell 1010
EMFILE 1090endgrent() 182endpwent() 173endspent() 176endwin() 680ENMFILE 1090ENODEV 1090ENOENT 112, 1090ENOEXEC 1090ENOFILE 1089ENOMEM 1090ENOPATH 1089ENOTSAM 1089env 1221ERANGE 1089erase() 690errno 47, 112, 1089Ersatzmuster
Shell 972Escape-Sequenzen 676ESUCCESS 112etc 169–170, 174–175, 179–180,
184EUID 189EWOULDBLOCK 510EXDEV 1090exec
Shell 1043execl() 226, 1126execle() 226, 1126execlp() 226, 1126execv() 226, 1126execve() 226execvp() 226, 1126exit 1169
Shellkommando 963exit() 1101exp() 1094expand 1158export 991EZERO 1089
F
F_DUPFD 62F_GETFD 62F_GETFL 63F_GETLK 70
F_GETOWN 64F_RDLCK 70F_SETFD 63F_SETFL 64F_SETLK 70F_SETLKW 71F_SETOWN 64F_UNLCK 70F_WRLCK 70fabs() 1094Farbe 696fchdir() 95, 1121fchmod() 82, 1124fchown() 1124fclose() 85, 1098fcntl() 61, 67, 69, 287, 1114FD_CLR() 75, 464, 1115
Sicherheit 1085FD_ISSET() 75, 464, 1115FD_SET() 75, 464, 1115
Sicherheit 1085FD_ZERO() 75, 464, 1115fdformat 1184fdisk 1185fdopen() 83, 85, 1114feclearexcept() 1107fegetenv() 1108fegetexceptflag() 1107fegetround() 1108Fehlerbehandlung 112feholdexcept() 1108fenv 1107feof() 88–89, 1100feraiseexcept() 1107ferror() 88–89, 1100fesetenv() 1108fesetexceptflag() 1107fesetround() 1108fetestexcept() 1107feupdateenv() 1108fflush() 91, 214, 1098fg 250, 1174
Shell 1058fgetc() 88, 1099fgetpos() 90, 1100fgets() 88, 1100FIFO-Pipes 277, 303
Kommunikation nichtverw. Prozesse 311
file 1158File-Deskriptor
Shell 1043
Index
1229
Filedeskriptor 44/proc/$pid/fd/ 138Duplizieren 59, 62Eigenschaften abfragen 61Eigenschaften ändern 61
fileno() 83, 1114Filesystem 146FILE-Zeiger 84find 1159finger 1169float 1090floor() 1094FLT_DIG 1090FLT_EPSILON 1091FLT_MANT_DIG 1090FLT_MAX 1090FLT_MAX_10_EXP 1090FLT_MAX_EXP 1090FLT_MIN 1091FLT_MIN_10_EXP 1090FLT_MIN_EXP 1090FLT_RADIX 1090FLTK 848FLUID 849Flushing, fflush() 92Flusskontrolle 282fma() 1111fmax() 1110fmin() 1110fmod() 1095fold 1159fopen() 85, 1098fork() 211, 449, 1126Formatierte Ausgabe 86Formatierte Eingabe 87for-Schleife
Shell 1022fprintf() 86, 1099fputc() 88, 1099fputs() 88, 1100fread() 88, 1100free 1180free() 1101freopen() 85, 1098frexp() 1094fscanf() 87, 1099fsck 1186fseek() 57, 90, 1100fsetpos() 90, 1100fstat() 82, 115, 1123fsync() 81ftell() 90, 1100
ftok() 319FTP 414ftruncate() 81fts() 109ftw() 108, 1122FTW_D 109FTW_DNR 109FTW_F 109FTW_NS 109FTW_SL 109Funktion
Shell 1050Funktionsreferenz 1087
ANSI C 1087C99 1105Elementare E/A-Funktionen
1112Verzeichnis-Funktionen 1120
Funktionstasten 684fwrite() 88, 1100
G
g_array_append_vals() 736g_array_free() 736g_array_index() 736g_array_insert_vals() 736g_array_new() 736g_array_prepend_vals() 736g_array_remove_index() 736g_assert() 723g_error() 722g_filename_from_utf8() 832g_free() 725g_list_append() 809g_malloc() 725g_malloc0() 725g_memdup() 726g_message() 722g_new() 727g_new0() 727g_object_get() 784, 809g_object_new() 742, 752g_object_set() 784, 809g_print() 722g_printerr() 722g_realloc() 725g_renew() 727g_return_if_fail() 724g_return_val_if_fail() 724g_set_print_handler() 722g_set_printerr_handler() 722
g_signal_connect() 744–745g_signal_connect_swapped() 804g_snprintf() 729G_STR_DELIMITERS 730g_strcasecmp() 728g_strchomp() 730g_strchug() 730g_strconcat() 729g_strdelimit() 730g_strdown() 728g_strdup() 729g_strdup_printf() 729g_strdup_vprintf() 729g_strerror() 722g_string_append() 732g_string_append_c() 732g_string_assign() 732g_string_erase() 732g_string_free() 732g_string_insert() 732g_string_insert_c() 732g_string_new() 732g_string_prepend() 732g_string_prepend_c() 732g_string_sized_new() 732g_strjoin() 729g_strncasecmp() 728g_strndup() 729g_strreverse() 728g_strup() 728g_timer_destroy() 735g_timer_elapsed() 735g_timer_new() 734g_timer_reset() 735g_timer_start() 734g_timer_stop() 735g_try_malloc() 725g_try_realloc() 725g_warning() 722GCC 33, 859
Assembler 860Dateiendungen 862Debuggen 865Linken von Bibliotheken 861Linker 860Optimierungs-Optionen 865Präprozessor 859Präprozessor-Optionen 865Profiling 865Übersetztung in Einzelschritten
863Warnmeldungs-Optionen 864
Index
1230
GCOV 927GDB 865, 930
Ausführen 931Ausführung fortsetzen 934Datenausgabe 937Einzelschritte 935Haltepunkte setzen 933Haltepunkte verwalten 935Inhalt einer Datei anzeigen 932Programm übersetzten 930Variablen prüfen 938Variablen verändern 938
GDK 716Eventmaske 835Events 833Xlib 716
GdkPixbuf 764Gerätedatei 149
/dev-Verzeichnis 149block device 149character device 149major Nummer 150minor Nummer 150Namen 152Spezielle 153
GETALL 326getbegyx() 702getc() 88, 1099getch() 682getchar() 88, 1099getcwd() 97, 1121getdtablesize() 1080getegid() 209, 1125getenv() 201, 1101, 1125geteuid() 209, 1125getgid() 209, 1125getgrent() 182gethostbyaddr() 439, 1140gethostbyname() 439, 1140gethostbyname2() 1140getmaxyx() 702getmouse() 707GETNCNT 326getnstr() 684getopts
Shell 1004getpeername() 1138GETPID 326getpid() 209, 1125getppid() 209, 1125getpriority() 192getpwent() 173
getpwnam() 171getpwuid() 171getrlimit() 207, 1125getrusage() 209, 1125gets() 88, 1099getservbyname() 428, 443, 1141getservbyport() 443, 1141getsockname() 1138getsockopt() 486, 1137getspent() 176getspnam() 178getstr() 684getuid() 209, 1125GETVAL 326getyx() 693GETZCNT 326GID 189GIMP 715GLADE 844Gleitpunktzahlen
Kategorie (C99) 1111Makros (C99) 1111Vergleichen 1111
Gleitpunktzahlen-Umgebung 1107
Glib 716UTF-8 831
Glib Bibliothek 720Array (dynamisch) 736Assertions-Funktionen 723Ausgabe 722Binäre Bäume 740Datentypen 720Hashtabellen 740Listen 740Makros zur Speicherverwaltung
726Quarks 727Speicherblöcke kopieren 726Speicherklumpen 727Speicherverwaltung 725Stringbearbeitung 727Stringpuffer (dynamisch) 731Strings kopieren 729Timer 734
gmtime() 1104GNOME-Desktop 717GNU-Assembler 860GNU-Projekt 32GPROF 865, 924
Flaches Profil 925Profil anlegen 924
GPROF (Forts.)Strukturiertes Profil 926
groupadd 1170groupdel 1170Group-ID-Bit 119groupmod 1170groups 1170grp.h 180Gruppendatenbank 179GTK 715
GNOME 717gtk-config 719pkg-config 719Schnittstellen 717Übersetzten 719
GTK-- (gtkmm) 841GTK+ Bibliothek
Anwendung beenden 745Anzeige-Elemente 758Callbackfunktion 743Container 747Eigenschaften von Widgets 743Events 743, 748, 833Grafiken 764Grundlagen 741Initialisieren 741Klassen-Baum 744Pango 761Repertoire 783Ressource-Files 839Signal für Buttons 784Signale 743, 748, 839Stock Items 839Stock-Element 806Umlaute 749UTF-8 749Verarbeitungsschleife 748Verarbeitungsschleife beenden
749Widget erzeugen 742Widgets packen 747
GTK+ WidgetAbkürzungsbuchstabe 805Accelerator 801, 804–805Anzeigen 748Auswählen 824Bäume und Listen 838Behälter 747, 765, 767, 773Box 765Button 777Button-Box 772Checkbutton 777
Index
1231
GTK+ Widget (Forts.)Combobox 808Container 747Dateiauswahl 824, 831Dateneingabe 785Dialogbox 753Drag and Drop 839Eigenschaften 743Eigenschaften abfragen 784Eigenschaften verändern 784Erzeugen 742Farbauswahl 824Fenster 750Fenster-Eigenschaften 752Fortschrittbalken 765Grafiken anzeigen 764Hierarchische Anordnung 747Lineal 839Löschen 748Menü 795, 800Notizbuch 771Optionsmenü 808Packen 747Radio-Buttons 777, 783Schiebeleiste 770Schieberegler 785, 792Schriftauswahl 824Scrollendes Fenster 823Signale für Buttons 784Statusleiste 764Stellgröße 794Tabelle 773Tastatur-Shortcuts 804Text (mehrzeilig) 812Text(editor) 820Textdarstellung (Eigenschaften)
822Textfelder 785, 791Textlabel 760Textlabel-Eigenschaften 760Text-Tags (Eigenschaften) 820Textwidget-System 812Toolbar 795, 805Tooltipp 806Trennlinie 763Umschaltbutton 777Unterstriche 805Verstecken 748Zahlenfelder 785, 793Zwischenablage 839
gtk_adjustment_clamp_page() 794
gtk_adjustment_get_value() 794gtk_adjustment_new() 794gtk_adjustment_set_value() 794gtk_box_pack_defaults() 748gtk_box_pack_start_defaults()
766gtk_combo_set_popdown_
strings() 809gtk_container_add() 747gtk_dialog_new_with_buttons()
755gtk_dialog_run() 757gtk_exit() 749gtk_file_selection_get_selections()
832GTK_HSCALE 785gtk_init() 741gtk_item_factory_create_items()
800, 803gtk_item_factory_create_new()
800gtk_item_factory_get_widget()
803gtk_item_factory_new() 803gtk_item_factory_path_from_
widget() 803gtk_main() 748gtk_main_quit() 749gtk_menu_append() 808gtk_menu_item_new_with_
label () 808gtk_menu_shell_append() 808gtk_message_dialog_new() 807gtk_notebook_append_page_
menu() 771gtk_notebook_insert_page_
menu() 771gtk_notebook_prepend_page_
menu() 771gtk_notebook_remove_page()
771gtk_option_menu_get_history()
808gtk_option_menu_set_history()
808gtk_pack_box_end() 766gtk_pack_box_start() 766gtk_paned_add1() 770gtk_paned_add2() 770gtk_paned_pack1() 770gtk_paned_pack2() 770gtk_signal_connect() 744
gtk_statusbar_get_contex() 764gtk_statusbar_pop() 764gtk_statusbar_push() 764gtk_statusbar_remove() 764gtk_statusbar_set_has_resize_
grip() 764gtk_table_attach() 775gtk_table_attach_defaults() 775gtk_text_buffer_apply_tag_by_
name() 821gtk_text_buffer_copy_clipboard()
821gtk_text_buffer_create_tag() 820gtk_text_buffer_cut_clipboard()
821gtk_text_buffer_delete_selection()
821gtk_text_buffer_get_bounds()
832gtk_text_buffer_get_end_iter()
832gtk_text_buffer_get_selection_
bounds() 821gtk_text_buffer_get_text() 832gtk_text_buffer_insert() 832gtk_text_buffer_paste_clipboard()
821gtk_text_view_get_buffer() 820gtk_toolbar_append_space() 806gtk_toolbar_insert_stock() 806GTK_TYPE_ACCEL_GROUP 804GTK_TYPE_ADJUSTMENT 785,
794Signale 795
GTK_TYPE_BUTTON 777Eigenschaften 783
GTK_TYPE_CHECK_BUTTON 777Eigenschaften 783–784
GTK_TYPE_CLIPBOARD 813GTK_TYPE_COMBO
Eigenschaften 809GTK_TYPE_ENTRY 785, 791
Eigenschaften 791GTK_TYPE_FILE_SELECTION
Eigenschaften 832GTK_TYPE_HBOX 765
Eigenschaften 766GTK_TYPE_HBUTTON_BOX 772GTK_TYPE_HPANED 767, 770
Eigenschaften 770
Index
1232
GTK_TYPE_HSCALE 792Eigenschaften 792Signale 793
GTK_TYPE_HSEPARATOR 763GTK_TYPE_IMAGE 764GTK_TYPE_LABEL
Eigenschaften 760GTK_TYPE_MENU 808GTK_TYPE_NOTEBOOK 767,
771Eigenschaften 772
GTK_TYPE_OPTION_MENU 808GTK_TYPE_PROGRESS_BAR 765GTK_TYPE_RADIO_BUTTON
777, 783Eigenschaften 783–784
GTK_TYPE_RANGE 792GTK_TYPE_SCROLLED_
WINDOW 823Eigenschaften 823
GTK_TYPE_SPIN_BUTTON 785, 793Eigenschaften 793
GTK_TYPE_TABLE 773–774Eigenschaften 776
GTK_TYPE_TEXT_BUFFER 813GTK_TYPE_TEXT_ITER 813GTK_TYPE_TEXT_MARK 813GTK_TYPE_TEXT_TAG 813GTK_TYPE_TEXT_TAG_TABLE
813GTK_TYPE_TEXT_VIEW 813, 820GTK_TYPE_TOGGLED_BUTTON
777Eigenschaften 783–784
GTK_TYPE_TOOLBAR 795, 805Eigenschaften 805
GTK_TYPE_TOOLTIP 806GTK_TYPE_VBOX 765
Eigenschaften 766GTK_TYPE_VBUTTON_BOX 772GTK_TYPE_VPANED 767, 770
Eigenschaften 770GTK_TYPE_VSCALE 785, 792
Eigenschaften 792Signale 793
GTK_TYPE_VSEPARATOR 763GTK_TYPE_WINDOW 750, 752
Eigenschaften 752gtk_widget_add_accelerator()
804gtk_widget_destroy() 748
gtk_widget_hide() 748gtk_widget_hide_all() 748gtk_widget_set_event() 835gtk_widget_show() 748gtk_widget_show_all() 748gtk_window_add_accel_group()
804GtkAccelGroup 804GtkAdjustment 785, 794GtkBox 765GtkButton 777, 784GtkCellRenderer 838GtkCellRendererPixbuf 838GtkCellRendererText 838GtkCellRendererToggle 838GtkCheckButton 784GtkCheckButtons 777GtkClipboard 813, 839GtkColorSelection 824GtkCombo 808GtkDialog 753GtkEditable 795GtkEntry 785, 791, 793, 795, 808GtkFileSelection 824, 831GtkFontSelection 824GtkHBox 765GtkHButtonBox 772GtkHPaned 767, 770GtkHScale 785, 792GtkImage 758, 764GtkItemFactory 795, 800GtkItemFactoryEntry 800GtkItemFactoryEntry-Struktur
800GtkLabel 758, 760, 805GtkListStore 838GtkMenu 795, 808GtkMenuBar 795GtkMessageDialog 757, 807GtkNotebook 767, 771GtkOptionsMenu 808GtkProgressBar 765GtkRadioButton 777, 783–784GtkRange 792GtkRuler 839GtkScale 785GtkScrolledWindow 823GtkSeparator 758, 763GtkSpinButton 785, 793GtkStatusbar 758GtkTable 773–774GtkTextBuffer 813, 820
GtkTextIter 813GtkTextMark 813GtkTextTag 813GtkTextTagTable 813GtkTextView 813, 820GtkToggleButton 777, 784GtkToolbar 805GtkTooltip 806GtkTreeModel 838GtkTreeStore 838GtkTreeView 838GtkTreeViewColumn 838GtkVBox 765GtkVButtonBox 772GtkVPaned 767, 770GtkVScale 785, 792GtkWindow 750, 752–753, 807gunzip 1195gzip 1195
H
Halbgrafik 679halfdelay() 687halt 1203has_colors() 696head 1160herror() 441High Level 44Hintergrundprozess 249, 956
Hervorholen 1058hline() 701höhere Ebene 44host 1210Host Byte Order 425HOST_NOT_FOUND 441hostname 1206HP-UX 30–31htonl() 425, 1138htons() 425, 1138HTTP 414HTTPS 414hypot() 1111
I
I18n 39id 1170IEEE 37if
Shell 1006ifconfig 1207
Index
1233
IFSShellvariable 1035
IFTODT() 99inet_addr() 432inet_aton() 426, 432, 1139inet_lnaof() 435, 1139inet_makeaddr() 435, 1139inet_netof() 435, 1139inet_network() 434, 1139inet_ntoa() 433, 1139inet_ntop() 434, 1140inet_pton() 433, 1139info 1219init 242init_pair() 697initscr() 680init-Skript 242Inline-Eingabeumleitung 1033Inode, ermitteln 120INT_MAX 1091INT_MIN 1091Integer-Arithmetik
Shell 979Integertypen 1109Interprozesskommunikation 275
Benannte Pipes 303Benannte Pipes (FIFO-Pipe) 277FIFO-Pipes 303Lock Files 281Message Queue 278, 328Namenlose Pipe 276Namenlose Pipes 283Pipes 283Record Locking 281Semaphoren 279, 321Shared Memory 279, 339Sockets 281STREAMS 280System V IPC 318Unix-Domain-Sockets 496
inttypes 1109ioctl() 78, 151IP_ADD_MEMBERSHIP 488, 503IP_DROP_MEMBERSHIP 488,
503IP_HDRINCL 487IP_MULTCAST_IF 503IP_MULTICAST_IF 488IP_MULTICAST_LOOP 488, 504IP_MULTICAST_TTL 488, 504IP_OPTIONS 487IP_RECVDSTADDR 487
IP_RECVIF 487IP_TOS 487IP_TTL 487IPC_CREAT 324, 329, 340IPC_EXCL 324, 329, 340IPC_INFO 326, 331, 340IPC_PRIVATE 324, 329, 340IPC_RMD 331IPC_RMID 326, 340IPC_SET 326, 331, 340IPC_STAT 326, 331, 340IPv4 514
Portieren nach IPv6 515IPv6 514–515IPV6_ADD_MEMBERSHIP 488IPV6_ADDRFORM 488IPV6_CHECKSUM 488IPV6_DROP_MEMBERSHIP 488IPV6_DSTOPTS 488IPV6_HOPLIMIT 488IPV6_HOPOPTS 488IPV6_MULTICAST_HOPS 488IPV6_MULTICAST_IF 488IPV6_MULTICAST_LOOP 488IPV6_NEXTHOP 488IPV6_PKTINFO 488IPV6_PKTOPTIONS 488is_wintouched() 702isalnum() 1088isalpha() 1088isascii() 1088isatty() 669iscntrl() 1088isdigit() 1088isgraph() 1088islower() 1088iso646 1105isprint() 1088ispunct() 1088isspace() 1088isupper() 1088isxdigit() 1088
J
jmp_buf 1095jobs 1174
Shell 1059Jobverwaltung 249
bg 250fg 250Shell 1059
K
Keep Alive 480Kernelinformationen 139Kernel-Level 44keypad() 682kill 1174
Shell 1054kill() 256, 267, 1129killall 1174Kommando
adduser 1172afio 1190alias 1221apropos 1219arp 1206at 1173badblocks 1180basename 1168batch 1173bc 982, 1221bg 1060, 1174bunzip2 1190bzcat 1151bzip2 1190cal 1202cat 1151cd 1168cfdisk 1181chgrp 1152chmod 1153chown 1154cksum 1152clear 1217cmp 1154comm 1154compress 1190cp 1155cpio 1190cron 1174crypt 1193csplit 1156cut 984, 1156date 1202dd 1181dd_rescue 1183df 1179diff 1156diff3 1157dig 1210dircmp 1168dirname 1168
Index
1234
Kommando (Forts.)dmesg 1203dos2unix 1157Drucker- 1205du 1179dump 1193dumpe2fs 1183e2fsck 1184echo 1028env 1221exec 1043exit 1169expand 1158export 991fdformat 1184fdisk 1185fg 1058, 1174file 1158find 1159finger 1169fold 1159free 1180fsck 1186getopts 1004groupadd 1170groupdel 1170groupmod 1170groups 1170gunzip 1195gzip 1195halt 1203head 1160host 1210hostname 1206id 1170ifconfig 1207info 1219jobs 1059, 1174kill 1054, 1174killall 1174last 1170less 1160let 980ln 1161logname 1171logout 1169ls 1161mail 1208mailx 1208man 1219md5 1152md5sum 1152
Kommando (Forts.)mesg 1216mkdir 1168mkfs 1187mknod 1046mkswap 1187more 1161mount 1188mt 1196mv 1161netstat 1209newgrp 1171nice 1175nl 1162nohup 1175nslookup 1210od 1162pack 1197parted 1189passwd 1171paste 985, 1163pcat 1163pgrep 1176ping 1210Postscript 1221printenv 1221printf 1030prtvtoc 1189ps 1175pstree 1176pwd 975, 1169rcp 1211read 1031reboot 1204renice 1176reset 1217restore 1193r-Kommandos 1211rlogin 1211rm 1163rmdir 1169rsh 1211rsync 1214rwho 1211scp 1213set 1003setterm 1217shift 1002shutdown 1204sleep 1177sort 1163split 1164
Kommando (Forts.)ssh 1212ssh-keygen 1213stty 1217su 1177sudo 1178sum 1152swap 1180swapoff 1189swapon 1189sync 1189tac 1164tail 1047, 1165tar 1197tee 970, 1165test 1011time 1178top 1178touch 1165tr 986, 1166traceroute 1215trap 1055tty 1218type 1166typeset 980ufsdump 1193ufsrestore 1193umask 1166umount 1188unalias 1221uname 1203uncompress 1190uniq 1167unix2dos 1167unpack 1197unzip 1201uptime 1203useradd 1172userdel 1172usermod 1172uudecode 1209uuencode 1209wait 1058wall 1216wc 1167whatis 1220whereis 1167who 1172whoami 1172write 1216zcat 1168zip 1201
Index
1235
Kommando (Forts.)zless 1168zmore 1168
Kommandoausführung überprüfenShell 1007
Kommandosubstitution 998Kommandozeilenargument
Shell 999Kommandozeilenoptionen
auswertenShell 1004
KommunikationsmodellSocket 415
Korn-Shell 954ksh 954
L
L_ctermid 666L10N 39labs() 1101Large Files System 58last 1170lchown() 1124lconv-Struktur 1093LDBL_DIG 1090LDBL_EPSILON 1091LDBL_MANT_DIG 1090LDBL_MAX 1091LDBL_MAX_10_EXP 1090LDBL_MAX_EXP 1090LDBL_MIN 1091LDBL_MIN_10_EXP 1090LDBL_MIN_EXP 1090ldconfig 884ldiv() 1101less 1160let 980LEX 949libpq-fe 1146limits 47, 1091, 1112Link 120
abfragen 122dangling symlink 121Hart 121Symbolisch 121
link() 121LINK_MAX 1113Linker 860Linux 32
Geschichte 32
Linux (Forts.)Torvald 32
listen() 421, 1136Listing
add_db.c 597addr.c 435admin.c 595backward.c 58, 127baud.c 668cdrom.c 163child.c 212child2.c 214child3.c 215child4.c 217client.c (FIFO-Pipes) 315client.c (Multicast) 505client.c (TCP) 426client.c (TCP/linear) 451client.c (UDP) 494client_msq.c 336client_shm.c 346cpy_file.c 56cpy_file_mmap.c 1118cur1.c 681cur10.c 694cur11.c 698cur12.c 703cur13.c 708cur14.c 709cur15.c 710cur2.c 683cur3.c 685cur4.c 686cur5.c 688cur6.c 690cur7.c 691cur8.c 692cur9.c 693daemon.c 239dup_fd.c 60dynamisch.c 887environ1.c 200environ2.c 201eventloop1.c 193eventloop2.c 194exec_child.c 230exec1.c 227exec2.c 228exec3.c 228exec4.c 229exec5.c 229exec6.c 229
Listing (Forts.)fifo_buf.c 309fifo1.c 306fifo2.c 307fifo4.c 309file_size.c 124file_times.c 125filetest.c 113filetest2.c 113filter.c 292find_dir.c 107ftwalk.c 110get_env.c 201glib1.c 722glib2.c 724glib3.c 725glib4.c 728glib5.c 730glib6.c 733glib7.c 735glib8.c 737gtk1.c 750gtk1b.c 753gtk2.c 758gtk2b.c 762gtk3.c 767gtk3b.c 773gtk4.c 777gtk5.c 785gtk6.c 795gtk6b.c 809gtk7.c 813gtk8.c 824gtk9.c 836index_news.c 600kernelinf.c 141key_ftok.c 319keystroke.c 661list_wd.c 101logging.c 237login.c 592Login.html 590make_file.c 49memory.c 130, 942msq_header.h 332my_cgi.h 584my_eof.c 658my_find.c 232my_getch.c 663my_getpass.c 659my_getpid.c 137my_limit.c 208
Index
1236
Listing (Forts.)my_link.c 122my_programm.c 230my_setlocale.c 1092my_stat.c 117my_tty.c 666mychdir.c 96myequal.c 881myequal.h 881myinfo.c 133mymkdir.c 94mysql1.c 551mysql2.c 552mysql3.c 556mysql4.c 566non_block.c 510offsetof() 1097openCD.c 78ping_pong.c 266pipe1.c 285pipe2.c 288pipe3.c 290pipe3b.c 293play_fd.c 64poll_stdin_time.c 76polling_fifo.c 312popen1.c 295popen2.c 297postgre1.c 636postgre3.c 639, 645printme.c 300printme2.c 301prio.c 192prio_child.c 219proz_dat.c 210pserver.c (TCP/parallel) 455put_env.c 202put_env2.c 203reverse.c 615scan_dir.c 105search_db.c 603sem.c 322sende.c 666sender.c 313server.c (FIFO-Pipes) 316server.c (Multicast) 504server.c (TCP) 429server.c (TCP/linear) 449server.c (UDP) 492server_msq.c 334server_shm.c 344set_env.c 204
Listing (Forts.)setbaud.c 669shm_header.h 342sig.c 262sig_sync.c 271sperre.c 72strxcat.c 1096summe.c 616terminfo.c 674terminfo2.c 677testlist.c 882thserver.c (TCP/Thread) 482trash.c 66ugid.c 119unset_env.c 205waise.c 220wait.c 223waitpid.c 226write_file.c 52write_vec.c 79zombie.c 221
Little Endian 425ln 1161locale 1091localeconv() 1093localtime() 1104Lock Files 281lockf() 67, 74log() 1094LOG_ALERT 234LOG_AUTH 234LOG_AUTHPRIV 234LOG_CONS 235LOG_CRIT 234LOG_CRON 235LOG_DAEMON 234LOG_DEBUG 234LOG_EMERG 234LOG_ERR 234LOG_FTP 234LOG_INFO 234LOG_KERN 234LOG_LOCAL0 235LOG_LPR 234LOG_MAIL 234LOG_NDELAY 235LOG_NEWS 234LOG_NOTICE 234LOG_PERROR 235LOG_PID 235LOG_SYSLOG 234LOG_USER 234
LOG_UUCP 234LOG_WARNING 234log10() 1094log2() 1110logb() 1110Logischer Operator
Shell 1017logname 1171logout 1169LONG_MAX 1091LONG_MIN 1091longjmp() 1095Low Level 43ls 1161lseek() 57, 1114lstat() 115, 1123
M
mail 1208mailx 1208MAKE 866
Abhängigkeit 870Abkürzungen 874Implizite Regeln 876Installieren von Anwendungen
878Kommentare 869Makefile 868Makros 874Makros (Übersicht) 875Musterregeln 878TAbulator 869Variablen 875Ziel (target) 869
Makefile 868malloc() 1101man 1219Mandatory Locking 68, 74
mit Linux 68math 1094, 1110Mausprogrammierung 706MAX_CANON 1113MAX_INPUT 1113MB_LEN_MAX 1091md5 1152md5sum 1152memchr() 1102memcmp() 1102memcpy() 1102memlockall() 1120memmove() 1102
Index
1237
Memory Leaks 942efence 942valgrind 945
Memory Mapped Ein/Ausgabe 1115
memset() 1102Mesa 3D 854mesg 1216Message Queue 278, 328
Ändern 331Erfragen 331Erzeugen 329Löschen 331Nachricht empfangen 330Nachricht versenden 329Öffnen 329struct msqid_ds 331
mkdir 1168mkdir() 94, 1121mkfifo 1046mkfifo() 305, 1130mkfs 1187mknod 1046mknod() 308mkstemp() 93mkswap 1187mktemp() 93mktime() 1104mlock() 1120mmap() 1116modf() 1094more 1161mount 1188mouseinterval() 709mousemask() 706move() 686MSG_WAITALL 446msgctl() 331, 1130msgget() 329, 1130msgrcv() 330, 1130msgsnd() 329, 1130msync() 1118mt 1196Multicast-Socket 502
Anwendungsgebiete 509Multiplexing 75, 1114Multiplexing I/O 462munlock() 1120munlockall() 1120munmap() 1117Mutexe 370mv 1161
mvdelch() 690mvgetch() 682mvgetnstr() 685mvgetstr() 685mvprintw() 686mvwin() 702mvwprintw() 700my_ulonglong 1146MYSQL 1145MySQL 522
Anwendungsgebiete 523Autowerte definieren 538Benutzer entfernen 528Benutzer hinzufügen 528Daten ändern 541Daten ausgeben 543Daten einfügen 541Daten importieren 542Daten löschen 542Datenbank anlegen 534Datenbank löschen 534Datenbank verwenden 534Datentypen 531DDL-Befehle 531DML-Befehle 531Grafische Clients 530Indicies 537Installation 524Kommandozeilenwerkzeuge 526Konfigurationsdatei 525my.cnf 525mysql (Client) 527mysqladmin 527MySQLCC 530mysqldump 529mysqlshow 529NULL 545Passwort einrichten 528phpMyAdmin 530Schlüsselfelder 536Schnittstellen 524Server starten/stoppen 524Tabelle ändern 538Tabelle anlegen 535Tabelle umbenennen 538Tabellentypen 538UDF-Schnittstelle 610Unscharfe Suche 545
MySQL C-API 546Benutzer wechseln 577Datenbanknamen abfragen 559Ergebnismenge 572
MySQL C-API (Forts.)Ergebnissmenge bearbeiten 560Fehlerbehandlung 549Fehlercodes 549Feldcursor abfragen 565Feldcursor platzieren 565Informationen abfragen 555my_ulonglong 561MYSQL_FIELD 562MYSQL_FIELD_OFFSET 565MYSQL_RES 558MYSQL_ROW 560MYSQL-Objekt initialisieren 547Server-Threads ermitteln 559Spaltenweise abarbeiten 562SQL-Befehle an den Server 573Tabellennamen abfragen 559Veraltete Funktionen 582Verbindung herstellen 548Verbindung schließen 550Weitere Funktionen 577Zeilencursor abfragen 561Zeilencursor platzieren 561Zeilenweise abarbeiten 560Zugangsdaten 583
mysql_affected_rows() 576, 1141mysql_change_user() 577, 1142mysql_character_set_name()
1142mysql_close() 550, 1141mysql_connect() 549, 1141mysql_create_db() 1142mysql_data_seek() 572, 1142mysql_debug() 582, 1142mysql_drop_db() 1142mysql_dump_debug_info() 582,
1142mysql_eof() 1142mysql_errno() 549–550, 1142mysql_error() 550, 1142mysql_escape_string() 578, 1142mysql_fetch_field() 564, 1142mysql_fetch_field_direct() 564,
1143mysql_fetch_fields() 564mysql_fetch_lengths() 573mysql_fetch_row() 560, 1143MYSQL_FIELD 1146mysql_field_count() 565, 1143MYSQL_FIELD_OFFSET 1146mysql_field_seek() 565, 1143mysql_field_tell() 565, 1143
Index
1238
mysql_free_result() 559, 577, 1143
mysql_get_client_info() 555, 1143
mysql_get_host_info() 555, 1143mysql_get_proto_info() 555,
1143mysql_get_server_info() 556,
1143mysql_info() 556, 1143mysql_init() 547, 1143mysql_insert_id() 578, 1143mysql_kill() 1144mysql_list_dbs() 559, 1144mysql_list_fields() 559, 1144mysql_list_processes() 559, 1144mysql_list_tables() 1144mysql_num_fields() 560, 566,
1144mysql_num_rows() 561, 1144mysql_options() 579, 1144mysql_ping() 580, 1144mysql_query() 573, 1144mysql_real_connect() 548–549,
1144mysql_real_escape_string() 578,
1142mysql_real_query() 573, 1145mysql_reload() 1145MYSQL_RES 1145MYSQL_ROW 1145MYSQL_ROW_OFFSET 561mysql_row_seek() 561, 1145mysql_row_tell() 561, 1145mysql_select_db() 581, 1145mysql_shutdown() 581, 1145mysql_stat() 1145mysql_store_result() 565, 574,
1145mysql_thread_id() 581, 1145mysql_thread_safe() 1145mysql_use_result() 575, 1145MySQL-Befehl
ALTER TABLE 539Benutzerdefinierte Funktionen
615CREATE DATABASE 534CREATE FUNCTION 618CREATE TABLE 535DELETE 542DROP DATABASE 535DROP FUNCTION 618
MySQL-Befehl (Forts.)Eigene Funktionen schreiben 609Erweitern in C 609EXPLAIN 538GRANT 528INSERT INTO 541LOAD DATA INFILE 542MODIFY 540REVOKE 528SELECT 543SHOW DATABASES 534UPDATE 541USE 535
N
NAME_MAX 1113Named Pipes
Shell 1046Namen Server 439Namen-Expansion
Shell 972nanosleep() 269, 1129ncurses
ACS_-Sonderzeichen 686Ausgabe 685Beenden 680Bildschirm ab Cursor löschen
690Bildschirm löschen 690Cursor positionieren 686Cursortasten 684Eingabe 682Farbe 696Farbenpaar 697Fenster als Verändert markieren
702Fenster duplizieren 701Fenster freigeben 701Fenster verschieben 702Fenstereigenschaften 691Fenstergröße ermitteln 702Fensterinhalt kopieren 703Fensterroutinen 700Funktionstasten 684Initialisieren 680Linie zeichnen 701Mausprogrammierung 706Position des Cursors ermitteln
693Rahmen zeichnen 701raw-Modus 682
ncurses (Forts.)Schriftattribute 696Scrolling 691, 693Sonderzeichen 687String ausgeben 689String einlesen 684Tastaturmodus 681Weitere Fenster 701y/x-Bezugspunkt ermitteln 702Zeichen am Cursor löschen 690Zeichen ausgeben 687Zeile ab Cursor löschen 690Zeile einfügen 686Zeile löschen 686
ncurses-Bibliothek 679netdb 1140netinet 1138netstat 1209Network Byte Order 425Netzwerkprogrammierung 407
Adressfamilie 416Big-Endian 425Clientanwendung 424Clientanwendung (UDP) 491Daten empfangen 422, 492Daten senden 422, 492Datenformat 461Host Byte Order 425IPv4 514IPv4 nach IPv6 515IPv6 514Konverter-Funktionen 432Little-Endian 425Multicast-Socket 502Network Byte Order 425Nichtblockierende Socket 509Parallele Server 448Protokollfamilie 416Pufferung 446Raw Socket 512RPC 513Serveranwendung 427Serveranwendung (UDP) 492Sicherheit 517Socket 414Socket an Port binden 420Socket anlegen 416Socket-Optionen 485Socketschnittstelle 416Socket-Typen 417Systemabhängiges 425Threads 481
Index
1239
Netzwerkprogrammierung (Forts.)TLI 513UDP 489Verbindung schließen 423Verbindungsaufbau (Client) 418Verbindungswünsche abarbeiten
421Verschlüsselung 517Warten auf Verbindung 421XTI 513
NetzwerktechnikAnwendungsschicht 409Bitübertragungsschicht 408DNS 411Internet (www) 410Kommunikationsmodell 415Kommunikationsschicht 409Netzklasse 434Netzwerknummer 434Ports 413Präsentationsschicht 409Protokolle 413Refernzmodell 408RFC 413Sicherungsschicht 409TCP/IP 411TCP/IP-Schichtenmodell 409Transportschicht 409Vermittlungsschicht 409
newgrp 1171newwin() 701nftw() 1122NGROUPS_MAX 1113nice 1175Nichtblockierende Socket 509NIS 184nl 1162NLS 39NO_ADDRESS 441NO_DATA 441NO_RECOVERY 441nocbreak() 681noecho() 682nohup 1175noraw() 682nslookup 1210ntohl() 425, 1138ntohs() 425, 1138NTP 414
O
O_ACCMODE 63O_APPEND 47–48, 57O_ASYNC 54, 63O_CREAT 48, 51O_EXCL 48O_LARGEFILE 58O_NDELAY 287O_NOCTTY 48O_NONBLOCK 48, 75, 287, 308O_RDONLY 47O_RDWR 47O_SYNC 48, 54, 81O_TRUNC 48, 51, 82O_WRONLY 47od 1162off_t 58offsetof() 1097open() 46, 1114OPEN_MAX 47, 52, 76, 1113opendir() 100, 1121OpenGL 854openlog() 235OSI-Schichtenmodell 408overlay() 703overwrite() 703
P
pack 1197pair_content() 698Pango 761parted 1189PASS_MAX 1113passwd 169, 1171
Struktur 170Passwortdatei 169, 174paste 1163
Shell 985PATCH 949PATH_MAX 1113pause() 260, 268, 1129pcat 1163pclose() 1129pdksh 954perror() 47, 1100PF_APPLETALK 417PF_ATMPVC 417PF_AX25 417PF_INET 416PF_INET6 416
PF_IPX 416PF_LOCAL 416PF_NETLINK 416PF_PACKET 417PF_UNIX 416PF_X25 417pgrep 1176PID 189PID_MAX 1113ping 1210Pipe
Shell 969Pipe (benannt) 277pipe() 283, 1129PIPE_BUF 291, 308, 1113Pipes 283
Drucken mit lpr 299Eigenschaften 283elementare Ein-/Ausgabe 287Filterprogramm 292Mail versenden 296Standard Ein-/Ausgabe 287Umleiten 289
Pipes (benannt) 303Pipes (namenlos) 276PIPESTATUS
Shell 1008POP3 414popen() 295, 1079, 1129Position des Cursors ermitteln
693Positionsparameter
Shell 1000POSIX 37POSIX_CHOWN_RESTRICTED
1113POSIX_JOB_CONTROL 1113POSIX_NO_TRUNC 1113POSIX_PRIORITY_SCHEDULING
194POSIX_SAVED_IDS 1113POSIX_SOURCE 1113POSIX_VERSION 1113PostgreSQL 619
.pgpass 651Benutzerverwaltung 623Daten ausgeben 629Daten hinzufügen 628Datenbank verwenden 626Datentypen 624Grafische Frontends 627im Vergleich mit MySQL 619
Index
1240
PostgreSQL (Forts.)Installieren 621Konfigurationsdateien 622Passwortdatei 651pg_hba.conf 622phpPgAdmin 627postgresql.conf 622Server starten/stoppen 624Syntax 620Tabelle anlegen 628Tabelle löschen 628Umgebungsvariablen 651Unscharfe Suche 630
PostgreSQL C-API 631Anfrage an den Server 637Anwendung übersetzen 631Ergebniss einer Anfrage 638Fehlerbehandlung 634Informationen zur Verbindung
633NULL 643Rückgabe einer Anfrage auslesen
642Status der Verbindung 633Status einer Anfrage 638Statuszeichenkette einer Anfrage
ermitteln 644Threads 652Verbindung herstellen 632Verbindung schließen 635Verbindung wiederaufnehmen
635PostgreSQL-Befehl
CREATE TABLE 628DELETE FROM 629DROP TABLE 628INSERT INTO 628SELECT 629UPDATE 629
Postscript-Kommandos 1221pow() 1095PPID 189PQbackendPID() 635, 1147PQbinaryTuples() 639, 1148PQclear() 639, 1148PQcmdStatus() 644, 1149PQcmdTuples() 644, 1149PQconnectdb() 632, 1146PQconnectPoll() 632, 1146PQconnectStart() 632, 1146PQdb() 635, 1147PQerrorMessage() 634
PQexec() 637, 1148PQfinish() 635, 1147PQfmod() 639, 1148PQfname() 638, 1148PQfnumber() 639, 1148PQfsize() 639, 1148PQftype() 639, 1148PQgetisnull() 643, 1149PQgetlength() 643, 1149PQgetssl() 635, 1147PQgetvalue() 643, 1149PQhost() 635, 1147PQnfields() 638, 1148PQntuples() 638, 1148PQoidStatus() 644, 1149PQoidValue() 644, 1149PQoptions() 635, 1147PQpass() 635, 1147PQport() 635, 1147PQreset() 635, 1147PQresetPoll() 636, 1147PQresetStart() 636, 1147PQresStatus() 638, 1148PQresultErrorMessage() 638,
1148PQresultStatus() 638, 1148PQsetdb() 632, 1146PQsetdbLogin() 632, 1146PQsocket() 635, 1147PQstatus 633PQstatus() 1147PQtty() 635, 1147PQuser() 635, 1147Präprozessor 859printenv 1221printf
Shell 1030printf() 86, 1099proc 129, 131–133, 135–140,
145–146Profiling 865, 923
Laufzeit einzelner Codezeilen 927
Laufzeit von Funktionen 924Laufzeit von Prozessen 923
Prozess 187Adressraum 188Ausführung 242Auslagerung 195Benutzernummer (UID, EUID)
189cron-Job 252
Prozess (Forts.)Daemon 242Dämon- 232Deadlocks 282Eltern- 211Erzeugung 211Flusskontrolle 282Gruppennummer (GID, EGID)
189Hintergrund 249init 199, 242init-Skript 242Jobverwaltung 249Kenndaten 188Kind- 211Kind überlagern 230Kommando
ps 196Komplett ersetzen 226Lebenszyklus 198Limits 206Parallele Server (TCP) 448Priorität 191Priorität verändern 218Prozesserkennung 209Prozessnummer (PID) 189Prozessnummer des Vaters (PPID)
189Pufferung 214Race Condition 282Runlevel 242Scheduling-Priorität abfragen
192Scheduling-Priorität verändern
192Startup-Skripte 244Status 190Steuerterminal 195Stoppen 268, 270Stoppen (Zeit) 269Suspendieren 268, 270Suspendieren (Zeit) 269Swapping 195Synchronisieren (Signale) 271Timesharing 191Überwachen 196Umgebungsvariablen 200Vererbung 218Verhungern 282Warten 220Warteschleife 193Zeitgesteuert ausführen 252
Index
1241
Prozess (Forts.)Zombie 199Zugriffsdisziplinen 282Zustände 190
ProzesseSignale 257
Prozesstabelleneintrag 45, 256prtctoc 1189ps 1175pstree 1176pthread 1132pthread_attr_destroy() 362, 1133pthread_attr_getdetachestate()
362pthread_attr_getdetachstate()
1133pthread_attr_getinheritsched()
367, 1133pthread_attr_getschedparam()
367pthread_attr_getschedpolicy()
367, 1133pthread_attr_init() 362, 1133pthread_attr_setdetachestate()
362pthread_attr_setdetachstate()
1133pthread_attr_setinheritsched()
367, 1133pthread_attr_setschedparam()
367pthread_attr_setschedpolicy()
366, 1133pthread_cancel() 391, 1135PTHREAD_CANCEL_ASYNCHRO
NOUS 391PTHREAD_CANCEL_DEFERRED
391PTHREAD_CANCEL_DISABLE
391pthread_cleanup_pop() 354pthread_cleanup_push() 354pthread_cond_broadcast() 378,
1135pthread_cond_destroy() 382,
1135pthread_cond_init() 382, 1135PTHREAD_COND_INITIALIZER
378pthread_cond_signal() 378, 1135pthread_cond_timedwait() 378pthread_cond_timewait() 1135
pthread_cond_wait() 378, 1135pthread_condattr_destroy() 387pthread_condattr_init() 387pthread_create() 352, 1132PTHREAD_CREATE_DETACHED
362–363PTHREAD_CREATE_JOINABLE
362pthread_detach() 361, 1132pthread_equal() 359, 1132pthread_exit() 353, 1132PTHREAD_EXPLICIT_SCHED 367pthread_getschedparam() 364pthread_getspecific() 396PTHREAD_INHERIT_SCHED 367pthread_join() 354, 1132pthread_key_create() 396pthread_key_delete() 396pthread_kill() 401pthread_mutex_destroy() 374,
1134PTHREAD_MUTEX_
ERRORCHECK_NP 377PTHREAD_MUTEX_FAST_NP
377pthread_mutex_init() 374PTHREAD_MUTEX_INITIALIZER
371pthread_mutex_lock() 371,
1133–1134PTHREAD_MUTEX_
RECURSIVE_NP 377pthread_mutex_trylock() 371,
1134pthread_mutex_unlock() 371,
1134pthread_mutexattr_destroy()
377, 1134pthread_mutexattr_getkind_np()
1134pthread_mutexattr_gettype()
377, 1134pthread_mutexattr_init() 377,
1134pthread_mutexattr_setkind_np()
1134pthread_mutexattr_settype() 377,
1134pthread_once() 398PTHREAD_ONCE_INIT 399pthread_self() 354, 1132
pthread_setcancelstate() 391, 1135
pthread_setcanceltype() 391, 1135
pthread_setschedparam() 364pthread_setspecific() 396pthread_sigmask() 401pthread_testcancel() 391ptrintw() 686Puffer 54–55Puffer kontrollieren 91Puffereinstellung
ANSI C 92SVR4 92
Pufferüberlauf 942, 1071putc() 88, 1099putchar() 88, 1099putenv() 202, 1125putp() 677puts() 88, 1100pwd 975, 1169pwd.h 170
Q
qsort() 1102Qt 849Qt-Designer 851Quotings
Shell 997
R
Race Condition 282, 1073raise() 256, 267, 1129rand() 1101ranlib 882Raw Socket 512raw() 682rcp 1211RCS 898–899
Auschecken 902Auschecken (read-only) 903Auschecken älterer Version 904Einchecken 902Konzept 900rcsdiff 907Revisionsnummer erzwingen
905Revisonsnummer verändern 905Schlüsselwörter 907Versionen vergleichen 907
Index
1242
RCS (Forts.)Versionsbäume 901Versionsprotokoll (rlog) 905Zugriffsliste 906
readShell 1031
read() 55, 287, 422, 1114readdir() 100, 1121readdir_r() 103readlink() 122readv() 79realloc() 1101reboot 1204Record Locking 66, 281
exclusiv locks 67shared locks 67
recv() 423, 1137recvfrom() 492, 1137Referenz 1087refresh() 686, 691regcomp() 106regerror() 106regex 106regex_t 106regexec() 106regfree() 106remove() 92, 1098rename() 92, 1098renice 1176reset 1217Ressourcenlimits 206restore 1193rewind() 90, 1100rewinddir() 103, 1121RFC 413rint() 1110r-Kommandos 1211RLIM_INFINITY 206RLIMIT_CORE 207RLIMIT_CPU 207RLIMIT_DATA 207RLIMIT_FSIZE 207RLIMIT_LOCKS 207RLIMIT_MEMLOCK 207RLIMIT_NOFILE 207RLIMIT_NPROC 207RLIMIT_RSS 207RLIMIT_STACK 207rlogin 1211rm 1163rmdir 1169rmdir() 93, 97, 1121
round() 1110RPC 513RPM 888
Benötigte Komponenten 890Build-Abschnitt 894Einführung 889Files-Sektion 894Install-Abschnitt 894Präambel 892Paket erstellen 895Paket installieren 897Patches 890Prep-Abschnitt 893Sourcecode 890–891Specfile 890, 892Verzeichnisse 890
rsetlimit() 47rsh 1211rsync 1214Runlevel 242rwho 1211
S
S_IRGRP 49, 116, 1123S_IROTH 49, 117, 1123S_IRUSR 48, 116, 1123S_IRWXG 49S_IRWXO 49S_IRWXU 49S_ISBLK() 116, 1123S_ISCHR() 116, 1123S_ISDIR() 116, 1123S_ISFIFO 285, 305S_ISFIFO() 116, 1123S_ISGID 48S_ISLINK() 116, 1123S_ISREG() 116, 1123S_ISSOCK() 116, 1123S_ISUID 48S_ISVTX 48S_IWGRP 49, 117, 1123S_IWOTH 49, 117, 1123S_IWUSR 48, 116, 1123S_IXGRP 49, 117, 1123S_IXOTH 49, 117, 1123S_IXUSR 48, 116, 1123SA_NOCLDSTOP 262SA_NOCLDWAIT 262SA_NODEFER 262SA_NOMASK 262SA_ONESHOT 262
SA_ONSTACK 262SA_RESETHAND 262SA_RESTART 262SA_SIGINFO 262scalb() 1111scandir() 104, 1121scanf() 87, 1099SCHAR_MAX 1091SCHAR_MIN 1091SCHED_FIFO 363SCHED_OTHER 363SCHED_RR 363sched_setpriority() 269sched_yield() 193Schleife
Shell 1022SCM-Systeme 899scp 1213scrl() 694scroll() 693scrollok() 691SDL 852sed 987SEEK_CUR 57, 90SEEK_END 57, 90SEEK_SET 57, 90seekdir() 103, 1121select
Shell 1047select() 75, 269, 463, 1114, 1129
Sicherheit 1085sem_destroy() 388sem_getvalue() 388sem_init() 387sem_post() 387sem_trywait() 387SEM_UNDO 327sem_wait() 387Semaphore
Threads 387Semaphoren 279, 321
Abfragen 325Ändern 325Erstellen 324Lebenszyklus 321Löschen 325Öffnen 324Operationen 326struct sembuf 327Vergleich mit Sperren 328
semctl() 321, 325, 1131semget() 321, 324, 1131
Index
1243
Semigrafik 679semop() 321, 326, 1131send() 423, 1136Sendmail 296sendto() 492, 1137Sequencing 415set
Shell 1003SETALL 326setbuf() 91, 1098setbuffer() 92setegid() 1126setenv() 204, 1125seteuid() 1126setgid() 210, 1125setgrent() 182set-group-ID Bit 48setjmp 1095setjmp() 1095setlinebuf() 92setlocale() 1092setpriority() 192, 218setpwent() 173setregid() 1126setreuid() 1126setrlimit() 207, 1125setscrreg() 692setsid() 238setsockopt() 431, 486, 507, 1137setspent() 176setterm 1217setuid() 210, 1125setupterm() 673set-user-ID Bit 48SETVAL 326setvbuf() 91, 1099sh 953shadow.h 175Shared Libraries 883Shared Memory 279, 339
Abfragen 340Ändern 340Erstellen 339Löschen 340Öffnen 339Segement loslösen 341Segment anbinden 341shmctl() 340
She-Bang-Zeile 959Shell 951
Array 989A-Shell 954
Shell (Forts.)Auf Prozess warten 1058Ausgabe 1028Auto-Variable 995Bash 954Bourne-Shell 953case 1020C-Shell 953dash 954Dateistatus ermitteln 1015Datenstrom umleiten 965Dezimalzahl 979Dezimalzahlen vergleichen 1012Eingabe 1031Einzelnes Zeichen einlesen 1039elif-Anweisung 1010else-Alternative 1010Festlegen 958for-Schleife 1022Funktion 1050if-Anweisung 1006IFS (Shell-Variable) 1035Jobverwaltung 1059Kommandosubstitution 998Kommandozeilenargument 999Korn-Shell 954Leerzeichen 997Logischer Operator 1017Menü mit select 1047Quotings 997Rechnen 982Schleife 1022Signal 1054tcsh 955test 1011Typ 953Umgebungsvariable 994until-Schleife 1026Variable 976Variableninterpolation 977while-Schleife 1025Zeichenkette 984Zeichenketten vergleichen 1014Zeilenumbruch 998Z-Shell 954
Shellprogrammierung 951Shellskript 951
Ausführen 955Beenden 963Datenstrom umleiten 965Im Hintergrund ausführen 956Kommunikation 1061
Shellskript (Forts.)ohne Subshell 957She-Bang-Zeile 959Subshell 956synchronisieren 1063Variable 976
shiftShell 1002
SHM_LOCK 340SHM_UNLOCK 340shmat() 341, 1131shmctl() 1131shmdt() 1131shmget() 339, 1131SHMMAX 340SHMMIN 340SHRT_MAX 1091SHRT_MIN 1091shutdown 1204shutdown() 1137Sicherheit 1067
Ausführrecht 1071chroot() 1075Core Dump 1082Filedeskriptoren 1080Logfiles 1068popen() 1079Race Condition 1073select() 1085Socketdeskriptoren 1080SQL Injection 1083SUID-Bit 1069Superuser 1068syslog() 1068system() 1079Temporäre Dateien 1074Trojaner 1067Umgebungsvariablen 1076Viren 1067Zugriffsrechte 1069, 1079
sig_atomic_t 265SIG_BLOCK 270, 402SIG_SETMASK 270, 402SIG_UNBLOCK 270, 402sigaction() 261, 1127sigaddset() 261, 1126sigdelset() 261, 1126sigemptyset() 260, 1126SIGEMT 258sigfillset() 260, 1126sighandler_t 261SIGIOT 258
Index
1244
sigismember() 1127SIGKILL 258, 402Signal
Shell 1054signal 1096signal() 1096Signale
Benutzerdefinierte 255Einrichten 261Erfragen 261exec-Aufruf 258fork() 257Gerätesignale 255Neues Signalkonzept 260pending signal 256Prozess suspendieren 270Prozesse synchronisieren 271Senden 267SIGABRT 258SIGALRM 259SIGBUS 258SIGCHLD 199, 257, 259, 454SIGCLD 259SIGCONT 259, 268SIGFPE 258SIGHUP 238, 258SIGILL 256, 258SIGINT 238, 258SIGIO 259SIGLOST 259Signalhandler 265Signalmaske 257, 269Signalmenge 260SIGPIPE 259, 285SIGPOLL 259SIGPROF 259SIGQUIT 258SIGSEGV 256, 258SIGSTOP 257, 259SIGSYS 258SIGTERM 258SIGTRAP 258SIGTSTP 259SIGTTIN 259SIGTTOU 259SIGURG 64, 259SIGUSR1 259, 271SIGUSR2 259, 271SIGVTALRM 259SIGWINCH 238, 259SIGXCPU 259SIGXFSZ 259
Signale (Forts.)Systemsignale 255Threads 401Zeitschaltuhr 268
Signale, SIGFPE 256Signale, SIGIO 64Signalhandler 261Signalmaske
Ändern 269Erfragen 269
SignalmengeHinzufügen 261Initialisieren 260Löschen 261
sigpending() 1127sigprocmask() 269, 1127sigset_t 260SIGSTOP 402sigsuspend() 270, 1127sigtimedwait() 402sigwait() 401sigwaitinfo() 402sin() 1094Single Quotings 997sinh() 1094size_t 52sleep 1177sleep() 269, 1129SMTP 414SNMP 414snprintf() 86SO_BROADCAST 487SO_DEBUG 487SO_DONTROUTE 487SO_ERROR 487SO_KEEPALIVE 487SO_LINGER 487SO_OOBINLINE 487SO_RCVBUF 487SO_RCVTIMEO 487SO_REUSEADDR 487SO_REUSEPORT 487SO_SNDBUF 487SO_SNDTIMEO 487SO_TYPE 487SO_USELOOPBACK 487SOCK_DGRAM 417SOCK_PACKET 417SOCK_RAW 417SOCK_RDM 417SOCK_SEQPACKET 417SOCK_STREAM 417
Socket 414Adressfamilie 416Auf Verbindung warten 421Clientanwendung 424Daten empfangen 422Daten senden 422Kommunikationsmodell 415Lauschen 421Lokale- 500Multicast- 502Multiplexing 462Namen Server 439Nichtblockierend 509Optionen abfragen 485Optionen setzen 485Parallele Server 448Port binden 420Protokollfamilie 416Pufferung 446, 448Raw- 512select() 463Serveranwendung 427sockaddr 420sockaddr_in 419sockaddr_un 497Socket-Deskriptor 418Socket-Typen 417Standard-E/A-Funktionen 447Struktur 419UDP 489Unix-Domain- 496Verbindung schließen 423Verbindungsaufbau (Client) 418Verbindungswünsche abarbeiten
421Webserver 471
socket() 416, 1136Socket-Deskriptor 418socketpair() 500, 1137Sockets 281
Threads 481Solaris 30–31Sonderzeichen 687sort 1163Sperrdateien 281split 1164sprintf() 86, 1099SQL 521SQL Injection 1083sqrt() 1094srand() 1101sscanf() 87, 1099
Index
1245
SSH 414ssh 1212ssh-keygen 1213ssize_t 52Standard E/A-Funktionen
Socket 447Standardausgabe
stdout 45STDOUT_FILENO 45
Standardausgabe umleitenShell 965
Standard-E/A-Funktionen 84Standardeingabe
stdin 45STDIN_FILENO 45
Standardeingabe umleitenShell 968
Standardfehlerausgabestderr 45STDERR_FILENO 45
Standardfehlerausgabe umleitenShell 966
start_color() 697Startup-Skripte erstellen 244stat (Struktur) 115
st_atime 125st_blksize 124st_blocks 124st_ctime 125st_gid 119st_ino 120st_mode 116st_mtime 125st_nlink 120st_size 124st_uid 119
stat() 115, 1123stat-Struktur 1122stdarg 87, 1096stdbool 1109stddef 1097stderr
Shell 966stdin
Shell 968stdint 1109stdio 84, 1098stdlib 1100stdout
Shell 965stdscr 682Steuerterminal 195
sticky Bit 48STRACE 939
Optionen 941strcat() 1103strchr() 1103strcmp() 1103strcoll() 1104strcpy() 1102strcspn() 1103STREAMS 280strerror() 47, 1103strftime() 1104string 1102strlen() 1103strncat() 1103strncmp() 1103strncpy() 1103strpbrk() 1103strrchr() 1103strspn() 1103strstr() 1103strtod() 1100strtok() 1103strtol() 1101strtoll() 1102strtoul() 1101strxfrm() 1104stty 1217su 1177Subshell 956
Explizit verwenden 1060sudo 1178SUID 189SUID-Bit 1069sum 1152Superuser 1068SVR4 39swap 1180swapoff 1189swapon 1189symlink() 121sync 1189Synchronisieren
Shellskripts 1063sys/mman 1116sys/socket 1136sys/stat 115, 1122syslog() 234, 1068System V IPC 318
Gemeinsamkeiten 318ipcrm 324ipcs 323
System V IPC (Forts.)Message Queue 328Semaphoren 321Shared Memory 339
system() 231, 1079, 1101, 1126Systemcalls 43
STRACE 939Systeminformationen 129
T
tac 1164tail 1165
Shell 1047tan() 1094tanh() 1094tar 1197tcgetattr() 657TCP/IP Aufbau 411TCP_KEEPALIVE 489TCP_MAXRT 489TCP_MAXSEG 489TCSADRAIN 657TCSAFLUSH 657TCSANOW 657tcsetattr() 657tcsh 955tee 970, 1165telldir() 103, 1121Telnet 414Temporäre Dateien 1074term 673termcap 672Terminal 653
ANSI-Steuersequenzen 672Attribute 654Ausgabeflags 656Baudrate ermitteln 667Baudrate verändern 669Bildschirm löschen 677curses 679Cursor positionieren 677Eingabeflags 655Escapesequenzen 676Fähigkeiten 672Farbe 696Flags löschen 659Flags setzen 659Geschwindigkeitskontrolle 667Identifizierung 665Kontrollflags 656Lokale Flags 656
Index
1246
Terminal (Forts.)Mausprogrammierung 706Modus 654Pfadname 666raw-Modus 663Semigrafik 679Sonderzeichen 657Sonderzeichen ändern 658Steuerzeichen 661termcap 672terminfo 671termios 653xterm 672
terminfo 671Eigenschaften anwenden 677Eigenschaften eines Terminals
674Escapesequenzen 676Fähigkeiten ermitteln 672Initialisieren 673
termios 653–654termios-Struktur 654test
Shell 1011tgmath 1112Thread-Programmierung 349Threads
Abbrechen 391Attribute 362Barrier 390Bedingungsvariablen 377Beenden 353Bibliotheken 350Canceln 391Conditions Variablen (dynamisch)
382Condition-Variablen 377Condition-Variablen (statisch)
378Condition-Variablen-Attribute
387Daemon- 361Einmaliges Ausführen 398Erzeugen 352Exit-Handler einrichten 353ID ermitteln 354Kernel- 351Loslösen 361Mutex-Attribute 377Mutexe 370Mutexe (dynamisch) 374Mutexe (statisch) 371
Threads (Forts.)Netzwerkprogrammierung 481Prozesse 349Rückgabewert 357RW-Locks 390Scheduling 351, 362Semaphore 387Signale 401Spinlocks 390Synchronisieren 367Thread-safe 400Threadspezifische Daten 395TSD-Daten 395Typen-Casting 357User- 351Vergleichen 359Warten 354Zustände 351
tigetflag() 674tigetnum() 674tigetstr() 674Tilde-Expansion
Shell 975TIME 923time 126, 1104, 1178time() 1104tmpfile() 93, 1098tmpnam() 93, 1098tm-Struktur 1105tolower() 1088Toolkit 715top 1178touch 1165touchwin() 702toupper() 1088tparm() 677tputs() 677tr 1166
Shell 986traceroute 1215trap
Shell 1055Trojaner 1067trunc() 1110truncate() 82TRY_AGAIN 441tty 1218ttyname() 666type 1166typeset
Shell 980
U
UCHAR_MAX 1091UDP 489
Clientanwendung 491Serveranwendung 492
ufsdump 1193ufsrestore 1193UID 189UID_MAX 1113UINT_MAX 1091ulimit() 47ULONG_MAX 1091umask 1166umask() 50, 1124Umgebungsvariable
Shell 994Umgebungsvariablen 200, 1076
Einzeln abfragen 201Hinzufügen 202Löschen 205Verändern 202
umount 1188unalias 1221uname 1203uname() 183uncompress 1190ungetc() 88, 1099ungetch() 685ungetmouse() 713uniq 1167unistd 1113Unix 30
Geschichte 30unix2dos 1167Unix-Domain-Sockets 496unlink() 93unpack 1197unsetenv() 205, 1125until-Schleife
Shell 1026unzip 1201uptime 1203userdel 1172User-ID-Bit 119User-Level 44usermod 1172USHRT_MAX 1091usleep() 269, 1129usr 859UTF-8 749, 831utime() 126
Index
1247
uudecode 1209uuencode 1209
V
va_arg() 1096va_end() 1096va_list 1096va_start() 1096valgrind 945Variable
Auto- (Shell) 995Exportieren 991Shell 976
VariableninterpolationShell 977
vasnprintf() 87vasprintf() 87versionsort() 104Versionsverwaltung 899
CVS 908RCS 899
Verzeichnis 94Arbeitsverzeichnis ermitteln 97Komplett einlesen 104Lesen 100Löschen 97Neu anlegen 94Öffnen 99Positionieren 103Schließen 100Verzeichnis-Bäume durchlaufen
108Wechseln 95
vfprintf() 86, 1099Viren 1067vline() 701v-node-Tabelle 46vprintf() 86, 1099vsnprintf() 86vsprintf() 86, 1099
W
waitShell 1058
wait() 199, 222, 1126waitpid() 222, 225, 454, 1126wall 1216wc 1167wchar 1106WCONTIUED 225WCOREDUMP() 223wctob() 1089wctype 1106Webserver
Apache 584wenclose() 708Werkzeuge 859wgetch() 682, 707whatis 1220whereis 1167while-Schleife
Shell 1025who 1172whoami 1172WIFEXITED() 223WIFSIGNALED() 223WIFSTOPPED() 223wmouse_trafo() 713WNOHANG 225, 458WNOWAIT 225wprintw() 700Wrapper 54wrefresh() 702write 1216write() 52, 287, 422, 1114writev() 79WSTOPSIG() 223WTERMSIG() 223WUNTRACED 225wxFormBuilder 847wxWidgets 845
X
X Window 852X/OPEN 37Xenix 32Xlib 852XOPEN_VERSION 1113
Y
YACC 949
Z
zcat 1168Zeichen einlesen
Shell 1039Zeichenkette
Shell 984Zeichenketten vergleichen
Shell 1014Zeichenweise E/A 88Zeilenweise E/A 88Zeitmessung
GCOV 923GPROF 923Laufzeit einzelner Codzeilen 927Laufzeit von Funktionen 924Laufzeit von Prozessen 923Profiling 923TIME 923
zgrep 1168zip 1201zless 1168zmore 1168zsh 954Z-Shell 954Zugriffsdisziplinen 282Zugriffsrechte 1069Zugriffsrechte erfragen 116