Facebook Pixel
Searching...
Deutsch
EnglishEnglish
EspañolSpanish
简体中文Chinese
FrançaisFrench
DeutschGerman
日本語Japanese
PortuguêsPortuguese
ItalianoItalian
한국어Korean
РусскийRussian
NederlandsDutch
العربيةArabic
PolskiPolish
हिन्दीHindi
Tiếng ViệtVietnamese
SvenskaSwedish
ΕλληνικάGreek
TürkçeTurkish
ไทยThai
ČeštinaCzech
RomânăRomanian
MagyarHungarian
УкраїнськаUkrainian
Bahasa IndonesiaIndonesian
DanskDanish
SuomiFinnish
БългарскиBulgarian
עבריתHebrew
NorskNorwegian
HrvatskiCroatian
CatalàCatalan
SlovenčinaSlovak
LietuviųLithuanian
SlovenščinaSlovenian
СрпскиSerbian
EestiEstonian
LatviešuLatvian
فارسیPersian
മലയാളംMalayalam
தமிழ்Tamil
اردوUrdu
The Linux Programming Interface

The Linux Programming Interface

A Linux and UNIX System Programming Handbook
von Michael Kerrisk 2010 1552 Seiten
4.63
500+ Bewertungen
Hören
Listen to Summary

Wichtige Erkenntnisse

1. Der Kernel: Das Herz des Systemprogrammierens

Das Kernbetriebssystem: Der Kernel.

Die Rolle des Kernels. Der Kernel ist das Herz des Betriebssystems und verwaltet die Ressourcen des Computers wie die CPU, den Speicher und die Geräte. Er fungiert als Vermittler zwischen Anwendungen und der Hardware und bietet eine konsistente Schnittstelle für Programme, um Aufgaben auszuführen. Der Kernel ist verantwortlich für die Prozessplanung, das Speichermanagement, den Zugriff auf das Dateisystem und die Interaktion mit Geräten.

Benutzer- vs. Kernel-Modus. Der Kernel arbeitet im privilegierten Modus, der ihm den Zugriff auf alle Speicher- und Hardware-Ressourcen ermöglicht. Benutzerprogramme laufen in einem eingeschränkten Modus, der sie daran hindert, direkt auf den Kernel-Speicher zuzugreifen oder privilegierte Operationen durchzuführen. Diese Trennung gewährleistet die Stabilität und Sicherheit des Systems.

Systemaufrufe. Benutzerprogramme interagieren mit dem Kernel über Systemaufrufe, die kontrollierte Einstiegspunkte in den Kernel darstellen. Diese Aufrufe ermöglichen es Programmen, Dienste vom Kernel anzufordern, wie z. B. Datei-I/O, Prozesscreation und Netzwerkkommunikation. Die Linux-Systemaufruf-API steht im Mittelpunkt dieses Buches.

2. Datei-I/O: Die universelle Schnittstelle

Universelle I/O.

Universelles I/O-Modell. UNIX-Systeme, einschließlich Linux, verwenden ein universelles I/O-Modell, bei dem dieselben Systemaufrufe (open(), read(), write(), close() usw.) für die Durchführung von I/O auf allen Arten von Dateien verwendet werden, einschließlich regulärer Dateien, Geräte, Pipes und Sockets. Diese Abstraktion vereinfacht das Programmieren und fördert die Wiederverwendbarkeit von Code.

Dateideskriptoren. Offene Dateien werden mit Dateideskriptoren bezeichnet, die kleine Ganzzahlen sind. Ein Prozess erbt drei Standard-Dateideskriptoren: 0 (Standard-Eingabe), 1 (Standard-Ausgabe) und 2 (Standard-Fehler). Diese Deskriptoren können auf andere Dateien oder Geräte umgeleitet werden.

Pufferung. Sowohl der Kernel als auch die stdio-Bibliothek führen eine Pufferung der Datei-I/O durch, um die Leistung zu verbessern. Der Kernel verwendet einen Puffer-Cache, um Daten im Speicher zu speichern und die Anzahl der Festplattenzugriffe zu reduzieren. Die stdio-Bibliothek verwendet ebenfalls Puffer, um die Anzahl der Systemaufrufe zu verringern. Das Verständnis der Pufferung ist entscheidend für das Schreiben effizienter und zuverlässiger Programme.

3. Prozesse: Die Grundlage der Ausführung

Prozesse und Programme.

Prozesse vs. Programme. Ein Programm ist eine Datei, die Anweisungen und Daten enthält, während ein Prozess eine Instanz eines ausführenden Programms ist. Der Kernel verwaltet Prozesse und weist Ressourcen wie CPU-Zeit, Speicher und Dateideskriptoren zu. Jeder Prozess hat eine eindeutige Prozess-ID (PID) und eine übergeordnete Prozess-ID (PPID).

Speicherlayout. Der virtuelle Speicher eines Prozesses ist in Segmente unterteilt: Text (Programmanweisungen), Daten (initialisierte globale Variablen), Heap (dynamisch zugewiesener Speicher) und Stack (Funktionsaufrufinformationen). Der Kernel verwaltet den virtuellen Speicher und stellt jedem Prozess seinen eigenen isolierten Adressraum zur Verfügung.

Prozesscreation und -beendigung. Ein neuer Prozess wird mit fork() erstellt, das den übergeordneten Prozess dupliziert. Der Kindprozess kann dann execve() verwenden, um ein neues Programm zu laden und auszuführen. Ein Prozess wird mit _exit() oder exit() beendet, und sein übergeordneter Prozess kann seinen Beendigungsstatus mit wait() abrufen.

4. Speichermanagement: Die Kunst der Zuweisung

Zuweisung von Speicher im Heap.

Heap-Zuweisung. Prozesse können dynamisch Speicher im Heap mit Funktionen wie malloc() und free() zuweisen. Der Heap ist ein Speicherbereich, der wächst und schrumpft, während Speicher zugewiesen und freigegeben wird. Der Kernel verwaltet den Heap, indem er den Programmbruch anpasst.

Stack-Zuweisung. Der Stack ist ein Speicherbereich, der zur Speicherung lokaler Variablen und von Funktionsaufrufinformationen verwendet wird. Die Funktion alloca() weist Speicher auf dem Stack zu, aber dieser Speicher wird automatisch freigegeben, wenn die Funktion zurückkehrt.

Speichermapping. Der mmap()-Systemaufruf erstellt ein Speichermapping, das verwendet werden kann, um eine Datei in den Speicher zu mappen oder um einen anonymen Speicherbereich zu erstellen. Speichermappings können zwischen Prozessen geteilt werden und bieten eine schnelle Methode der interprozessualen Kommunikation (IPC).

5. Zeit und Planung: Den Fluss steuern

Zeit.

Zeitkonzepte. UNIX-Systeme verwenden Kalenderzeit (Sekunden seit der Epoche) und Prozesszeit (CPU-Zeit, die von einem Prozess verwendet wird). Der Kernel verwaltet eine Softwareuhr, die die Zeit in Einheiten namens Jiffies misst.

Timer. Die Systemaufrufe setitimer() und alarm() richten Intervall-Timer ein, die Signale generieren, wenn sie ablaufen. Diese Timer können verwendet werden, um Zeitüberschreitungen bei blockierenden Operationen festzulegen.

Schlafen. Die Funktionen sleep() und nanosleep() unterbrechen die Ausführung eines Prozesses für einen bestimmten Zeitraum. Die POSIX-Funktion clock_nanosleep() bietet eine genauere Methode zum Schlafen, indem eine bestimmte Uhr verwendet wird.

Prozessplanung. Der Kernel-Scheduler bestimmt, welche Prozesse Zugriff auf die CPU erhalten. Prozesse haben einen Nice-Wert, der ihre Priorität beeinflusst. Echtzeit-Planungspolitiken (SCHED_RR und SCHED_FIFO) bieten eine genauere Kontrolle über die Prozessplanung.

6. Signale: Asynchrone Kommunikation

Signale: Grundlegende Konzepte.

Signalmechanismus. Signale sind eine Form der asynchronen Benachrichtigung an einen Prozess, dass ein Ereignis eingetreten ist. Signale können vom Kernel (z. B. Hardwareausnahmen, Terminaleingaben), von einem anderen Prozess oder vom Prozess selbst erzeugt werden.

Signalbehandlung. Ein Prozess kann entscheiden, ein Signal zu ignorieren, die Standardaktion zu akzeptieren (z. B. Beendigung) oder einen Signalhandler einzurichten, eine Funktion, die aufgerufen wird, wenn das Signal zugestellt wird.

Signalmaskierung. Ein Prozess kann die Zustellung bestimmter Signale blockieren, indem er sie zu seiner Signalmaske hinzufügt. Blockierte Signale bleiben ausstehend, bis sie wieder freigegeben werden. Signale werden nicht in einer Warteschlange gehalten; wenn dasselbe Signal mehrmals erzeugt wird, während es blockiert ist, wird es nur einmal zugestellt.

Signal-APIs. Die Funktion signal() ist die ältere API zur Einrichtung von Signalhandlern, ist jedoch nicht portabel. Die Funktion sigaction() ist die bevorzugte Methode zur Einrichtung von Signalhandlern, da sie mehr Kontrolle bietet und portabler ist.

7. Threads: Gleichzeitige Ausführung

Threads: Einführung.

Threads vs. Prozesse. Threads sind ein Mechanismus für die gleichzeitige Ausführung innerhalb eines einzelnen Prozesses. Threads teilen sich den gleichen virtuellen Speicher, Dateideskriptoren und Signaldispositionen, aber jeder Thread hat seinen eigenen Stack und seine eigene Thread-ID.

Pthreads-API. Die Pthreads-API bietet eine standardisierte Menge von Funktionen zum Erstellen, Beenden und Synchronisieren von Threads. Die Funktion pthread_create() erstellt einen neuen Thread, und pthread_exit() beendet einen Thread. Die Funktion pthread_join() wartet auf die Beendigung eines Threads.

Thread-Synchronisation. Threads verwenden Mutexes und Bedingungsvariablen, um den Zugriff auf gemeinsame Ressourcen zu synchronisieren. Mutexes bieten exklusiven Zugriff auf eine gemeinsame Variable, während Bedingungsvariablen es Threads ermöglichen, auf Änderungen im Zustand einer gemeinsamen Variable zu warten.

Thread-Sicherheit. Thread-sichere Funktionen können sicher von mehreren Threads gleichzeitig aufgerufen werden. Nicht-thread-sichere Funktionen sollten in mehrthreadigen Programmen vermieden werden. Thread-spezifische Daten und thread-lokaler Speicher bieten Mechanismen, um nicht-thread-sichere Funktionen thread-sicher zu machen, ohne ihre Schnittstellen zu ändern.

8. Interprozesskommunikation: Daten teilen und synchronisieren

Überblick über die Interprozesskommunikation.

IPC-Mechanismen. UNIX-Systeme bieten eine Vielzahl von Mechanismen für die Interprozesskommunikation (IPC), einschließlich Pipes, FIFOs, Nachrichtenwarteschlangen, Semaphoren, gemeinsamem Speicher und Sockets. Diese Mechanismen können verwendet werden, um Daten auszutauschen und die Aktionen von Prozessen zu synchronisieren.

Datenübertragung vs. gemeinsamer Speicher. Datenübertragungsanlagen (Pipes, FIFOs, Nachrichtenwarteschlangen, Sockets) beinhalten das Kopieren von Daten zwischen Prozessen, während gemeinsamer Speicher es Prozessen ermöglicht, direkt auf denselben Speicherbereich zuzugreifen. Gemeinsamer Speicher ist schneller, erfordert jedoch explizite Synchronisation.

System V vs. POSIX IPC. Die IPC-Mechanismen von System V (Nachrichtenwarteschlangen, Semaphore, gemeinsamer Speicher) sind älter und weiter verbreitet, aber ihre APIs sind komplexer und weniger konsistent mit dem traditionellen UNIX-I/O-Modell. Die POSIX-IPC-Mechanismen bieten eine einfachere und konsistentere API, sind jedoch nicht in allen UNIX-Implementierungen verfügbar.

9. Sockets: Vernetzung über Netzwerke

Sockets: Einführung.

Sockets als IPC. Sockets sind ein vielseitiger IPC-Mechanismus, der für die Kommunikation zwischen Prozessen auf demselben Host (UNIX-Domänensockets) oder zwischen Prozessen auf verschiedenen Hosts, die über ein Netzwerk verbunden sind (Internet-Domänensockets), verwendet werden kann.

Socket-Typen. Sockets gibt es in zwei Haupttypen: Streamsockets (SOCK_STREAM), die einen zuverlässigen, bidirektionalen Byte-Stream-Kommunikationskanal bieten, und Datagrammsockets (SOCK_DGRAM), die eine unzuverlässige, verbindungslose, nachrichtenorientierte Kommunikation bieten.

Socket-Operationen. Die wichtigsten Socket-Systemaufrufe sind socket() (erstellt einen Socket), bind() (bindet einen Socket an eine Adresse), listen() (markiert einen Streamsockel als passiv), accept() (akzeptiert eine Verbindung auf einem wartenden Streamsockel) und connect() (stellt eine Verbindung zu einem Peer-Socket her).

Internet-Domänensockets. Internet-Domänensockets verwenden IP-Adressen und Portnummern, um Kommunikationsendpunkte zu identifizieren. Die Funktionen getaddrinfo() und getnameinfo() werden verwendet, um zwischen Hostnamen und Dienstnamen sowie deren entsprechenden numerischen Darstellungen zu konvertieren.

10. Terminals und Pseudoterminals: Interaktion mit dem Benutzer

Terminals.

Terminalattribute. Terminals haben eine Reihe von Attributen, die steuern, wie sie arbeiten, einschließlich spezieller Zeichen, Flags und I/O-Modi. Diese Attribute können mit den Funktionen tcgetattr() und tcsetattr() abgerufen und geändert werden.

Terminal-I/O-Modi. Terminals können im kanonischen Modus (Eingabe zeilenweise) oder im nicht-kanonischen Modus (Eingabe zeichenweise) arbeiten. Der Terminal-I/O-Modus wird durch die Terminal-Flags gesteuert.

Pseudoterminals. Pseudoterminals (ptys) sind ein Paar verbundener virtueller Geräte, ein Master und ein Slave. Das Slave-Gerät bietet eine Schnittstelle, die sich wie ein Terminal verhält, während das Master-Gerät ein Mittel zur Steuerung des Slaves bereitstellt. Pseudoterminals werden in einer Vielzahl von Anwendungen verwendet, einschließlich Terminalfenstern und Netzwerk-Login-Diensten.

Terminalsteuerung. Der ioctl()-Systemaufruf wird verwendet, um eine Reihe von Steueroperationen auf Terminals durchzuführen, einschließlich der Einstellung von Terminalattributen, der Steuerung der Terminalleitung und der Abfrage der Terminalfenstergröße.

11. Sicherheit und Berechtigungen: Das System schützen

Sichere privilegierte Programme schreiben.

Privilegierte Programme. Privilegierte Programme haben Zugriff auf Systemressourcen, die normalen Benutzern nicht zur Verfügung stehen. Solche Programme sollten mit großer Sorgfalt geschrieben werden, um Sicherheitsanfälligkeiten zu vermeiden.

Minimalprivileg. Privilegierte Programme sollten mit dem minimalen Privileg arbeiten, das erforderlich ist, um ihre Aufgaben zu erfüllen. Das bedeutet, dass Privilegien fallen gelassen werden sollten, wenn sie nicht mehr benötigt werden, und dauerhaft fallen gelassen werden sollten, wenn sie nie wieder erforderlich sind.

Eingangsvalidierung. Privilegierte Programme sollten alle Eingaben aus untrusted Quellen sorgfältig validieren, einschließlich Befehlszeilenargumenten, Umgebungsvariablen und Daten aus Dateien und Netzwerkverbindungen.

Berechtigungen. Das Linux-Berechtigungsschema teilt das traditionelle Alles-oder-Nichts-Superuser-Privileg in verschiedene Einheiten, die Berechtigungen genannt werden. Dies ermöglicht es einem Prozess, nur die Privilegien zu erhalten, die er benötigt, und verringert so das Potenzial für Schäden, falls das Programm kompromittiert wird.

12. Gemeinsame Bibliotheken: Code-Wiederverwendbarkeit und Effizienz

Grundlagen gemeinsamer Bibliotheken.

Objektbibliotheken. Objektbibliotheken sind Dateien, die kompilierten Objektcode für eine Reihe von Funktionen enthalten. Statische Bibliotheken werden zur Compile-Zeit in ein ausführbares Programm verlinkt, während gemeinsame Bibliotheken zur Laufzeit geladen werden.

Gemeinsame Bibliotheken. Gemeinsame Bibliotheken ermöglichen es mehreren Prozessen, dieselbe Kopie des Bibliothekscodes im Speicher zu teilen, wodurch Speicherplatz auf der Festplatte und RAM gespart werden. Gemeinsame Bibliotheken ermöglichen auch, dass Bibliotheksaktualisierungen angewendet werden, ohne dass Programme neu verlinkt werden müssen.

Positionsunabhängiger Code. Gemeinsame Bibliotheken müssen mit positionsunabhängigem Code (PIC) kompiliert werden, der es dem Bibliothekscode ermöglicht, an jeder Adresse im Speicher geladen zu werden.

Dynamisches Verlinken. Der dynamische Linker ist verantwortlich für das Laden gemeinsamer Bibliotheken zur Laufzeit und das Auflösen von Symbolreferenzen. Die Umgebungsvariable LD_LIBRARY_PATH kann verwendet werden, um zusätzliche Verzeichnisse anzugeben, in denen der dynamische Linker nach gemeinsamen Bibliotheken suchen soll.

Symbol-Sichtbarkeit. Gemeinsame Bibliotheken sollten nur die Symbole exportieren, die Teil ihrer öffentlichen API sind. Versionsskripte können verwendet werden, um die Sichtbarkeit von Symbolen zu steuern und versionierte Symbole zu erstellen.

Zuletzt aktualisiert:

Rezensionen

4.63 von 5
Durchschnitt von 500+ Bewertungen von Goodreads und Amazon.

Die Linux-Programmierschnittstelle erhält überwältigend positive Bewertungen, wobei Leser die umfassende Abdeckung der Linux-Systemprogrammierung loben. Viele beschreiben es als ein definitives Nachschlagewerk und heben die klaren Erklärungen, praktischen Beispiele und den historischen Kontext hervor. Die Leser schätzen die Tiefe, Organisation und Lesbarkeit des Buches, trotz seiner beträchtlichen Größe. Es wird dafür gelobt, theoretische Konzepte mit praktischen Anwendungen zu verbinden, was es sowohl für das Lernen als auch als Referenz wertvoll macht. Viele Rezensenten haben viel Zeit mit dem Buch verbracht und fanden es lohnenswert, um die Interna von Linux und Systemaufrufe zu verstehen.

Über den Autor

Michael Kerrisk ist der Autor von „The Linux Programming Interface“ und seit 2004 der verantwortliche Betreuer des Linux man-pages-Projekts. Seine umfassende Erfahrung mit Linux-Systemen spiegelt sich in der umfassenden und autoritativen Natur des Buches wider. Kerrisks Schreibstil wird für seine Klarheit, Prägnanz und gute Struktur gelobt, wodurch komplexe Themen für die Leser zugänglich werden. Sein Engagement für das Thema wird in der gründlichen Behandlung des Inhalts und den Jahren harter Arbeit, die in die Erstellung des Buches geflossen sind, deutlich. Kerrisks Rolle bei der Pflege der Linux-Dokumentation hat wahrscheinlich zu seinem tiefen Verständnis des Systems beigetragen, das er effektiv durch sein Schreiben vermittelt.

0:00
-0:00
1x
Dan
Andrew
Michelle
Lauren
Select Speed
1.0×
+
200 words per minute
Create a free account to unlock:
Requests: Request new book summaries
Bookmarks: Save your favorite books
History: Revisit books later
Recommendations: Get personalized suggestions
Ratings: Rate books & see your ratings
Try Full Access for 7 Days
Listen, bookmark, and more
Compare Features Free Pro
📖 Read Summaries
All summaries are free to read in 40 languages
🎧 Listen to Summaries
Listen to unlimited summaries in 40 languages
❤️ Unlimited Bookmarks
Free users are limited to 10
📜 Unlimited History
Free users are limited to 10
Risk-Free Timeline
Today: Get Instant Access
Listen to full summaries of 73,530 books. That's 12,000+ hours of audio!
Day 4: Trial Reminder
We'll send you a notification that your trial is ending soon.
Day 7: Your subscription begins
You'll be charged on Mar 22,
cancel anytime before.
Consume 2.8x More Books
2.8x more books Listening Reading
Our users love us
100,000+ readers
"...I can 10x the number of books I can read..."
"...exceptionally accurate, engaging, and beautifully presented..."
"...better than any amazon review when I'm making a book-buying decision..."
Save 62%
Yearly
$119.88 $44.99/year
$3.75/mo
Monthly
$9.99/mo
Try Free & Unlock
7 days free, then $44.99/year. Cancel anytime.
Settings
Appearance
Black Friday Sale 🎉
$20 off Lifetime Access
$79.99 $59.99
Upgrade Now →