Kľúčové poznatky
1. Jadro: Srdce systémového programovania
Jadro operačného systému.
Úloha jadra. Jadro je srdcom operačného systému, ktoré spravuje zdroje počítača ako CPU, pamäť a zariadenia. Pôsobí ako sprostredkovateľ medzi aplikáciami a hardvérom, poskytujúc konzistentné rozhranie pre programy na vykonávanie úloh. Jadro zodpovedá za plánovanie procesov, správu pamäte, prístup k súborovému systému a komunikáciu so zariadeniami.
Režim používateľa vs. režim jadra. Jadro pracuje v privilegovanom režime, ktorý mu umožňuje prístup ku všetkej pamäti a hardvérovým zdrojom. Používateľské programy bežia v obmedzenom režime, ktorý im bráni v priamom prístupe do pamäte jadra alebo vo vykonávaní privilegovaných operácií. Toto oddelenie zabezpečuje stabilitu a bezpečnosť systému.
Systémové volania. Používateľské programy komunikujú s jadrom prostredníctvom systémových volaní, ktoré sú kontrolovanými vstupnými bodmi do jadra. Tieto volania umožňujú programom žiadať služby jadra, ako napríklad prácu so súbormi, vytváranie procesov alebo sieťovú komunikáciu. API systémových volaní Linuxu je hlavným predmetom tejto knihy.
2. Práca so súbormi: Univerzálne rozhranie
Univerzálnosť vstupu a výstupu.
Univerzálny model I/O. UNIXové systémy vrátane Linuxu používajú univerzálny model vstupu a výstupu, kde rovnaké systémové volania (open(), read(), write(), close() a ďalšie) slúžia na prácu so všetkými typmi súborov – bežnými súbormi, zariadeniami, rúrkami či soketmi. Táto abstrakcia zjednodušuje programovanie a podporuje opätovné použitie kódu.
Súborové deskriptory. Otvorené súbory sa označujú pomocou súborových deskriptorov, čo sú malé celé čísla. Proces dedí tri štandardné deskriptory: 0 (štandardný vstup), 1 (štandardný výstup) a 2 (štandardná chyba). Tieto deskriptory je možné presmerovať na iné súbory alebo zariadenia.
Buforovanie. Jadro aj knižnica stdio používajú na zlepšenie výkonu buferovanie vstupu a výstupu so súbormi. Jadro využíva vyrovnávaciu pamäť na ukladanie dát v pamäti, čím znižuje počet prístupov na disk. Knižnica stdio tiež používa buffery na zníženie počtu systémových volaní. Pochopenie buforovania je kľúčové pre písanie efektívnych a spoľahlivých programov.
3. Procesy: Základ vykonávania
Procesy a programy.
Procesy vs. programy. Program je súbor obsahujúci inštrukcie a dáta, zatiaľ čo proces je bežiaca inštancia programu. Jadro spravuje procesy, prideľuje im zdroje ako CPU čas, pamäť a súborové deskriptory. Každý proces má jedinečné ID procesu (PID) a ID rodičovského procesu (PPID).
Pamäťové rozloženie. Virtuálna pamäť procesu je rozdelená na segmenty: text (inštrukcie programu), dáta (inicializované globálne premenné), haldu (dynamicky alokovaná pamäť) a zásobník (informácie o volaniach funkcií). Jadro spravuje virtuálnu pamäť a poskytuje každému procesu izolovaný adresný priestor.
Vytváranie a ukončovanie procesov. Nový proces sa vytvára pomocou fork(), ktorý duplikuje rodičovský proces. Potom môže dieťa použiť execve() na načítanie a spustenie nového programu. Proces sa ukončuje pomocou _exit() alebo exit(), pričom rodič môže získať stav ukončenia pomocou wait().
4. Správa pamäte: Umenie prideľovania
Alokácia pamäte na halde.
Alokácia na halde. Procesy môžu dynamicky prideľovať pamäť na halde pomocou funkcií ako malloc() a free(). Halda je oblasť pamäte, ktorá rastie a zmenšuje sa podľa potreby alokácie a dealokácie. Jadro spravuje haldu úpravou programového zlomu.
Alokácia na zásobníku. Zásobník je oblasť pamäte používaná na ukladanie lokálnych premenných a informácií o volaniach funkcií. Funkcia alloca() alokuje pamäť na zásobníku, ktorá sa automaticky uvoľní po návrate z funkcie.
Pamäťové mapovania. Systémové volanie mmap() vytvára pamäťové mapovanie, ktoré môže slúžiť na mapovanie súboru do pamäte alebo na vytvorenie anonymnej pamäťovej oblasti. Pamäťové mapovania môžu byť zdieľané medzi procesmi, čo poskytuje rýchlu metódu medziprocesovej komunikácie.
5. Čas a plánovanie: Riadenie toku
Čas.
Koncepty času. UNIXové systémy používajú kalendárny čas (sekundy od Epochy) a procesný čas (CPU čas využitý procesom). Jadro udržiava softvérové hodiny, ktoré merajú čas v jednotkách nazývaných jiffies.
Časovače. Systémové volania setitimer() a alarm() nastavujú intervalové časovače, ktoré generujú signály po uplynutí času. Tieto časovače sa používajú na nastavenie časových limitov blokujúcich operácií.
Spánok. Funkcie sleep() a nanosleep() pozastavujú vykonávanie procesu na určený časový interval. POSIX funkcia clock_nanosleep() poskytuje presnejší spôsob spánku s využitím špecifikovaných hodín.
Plánovanie procesov. Plánovač jadra rozhoduje, ktoré procesy získajú prístup k CPU. Procesy majú hodnotu nice, ktorá ovplyvňuje ich prioritu. Realtime plánovacie politiky (SCHED_RR a SCHED_FIFO) umožňujú presnejšiu kontrolu nad plánovaním procesov.
6. Signály: Asynchrónna komunikácia
Signály: základné pojmy.
Mechanizmus signálov. Signály sú forma asynchrónneho upozornenia procesu, že nastala udalosť. Signály môžu generovať jadro (napr. hardvérové výnimky, vstup z terminálu), iný proces alebo samotný proces.
Spracovanie signálov. Proces môže signál ignorovať, prijať jeho predvolenú akciu (napr. ukončenie) alebo nastaviť obsluhu signálu – funkciu, ktorá sa vykoná pri doručení signálu.
Maskovanie signálov. Proces môže blokovať doručenie určitých signálov pridaním do svojej masky signálov. Blokované signály zostávajú čakajúce, kým nie sú odblokované. Signály sa neukladajú do frontu; ak je rovnaký signál generovaný viackrát počas blokovania, doručený bude len raz.
API pre signály. Funkcia signal() je staršie API na nastavenie obsluhy signálov, ale nie je prenosná. Funkcia sigaction() je preferovaná, pretože poskytuje väčšiu kontrolu a lepšiu prenosnosť.
7. Vlákna: Súbežné vykonávanie
Vlákna: úvod.
Vlákna vs. procesy. Vlákna umožňujú súbežné vykonávanie v rámci jedného procesu. Vlákna zdieľajú rovnakú virtuálnu pamäť, súborové deskriptory a správanie signálov, no každé vlákno má vlastný zásobník a ID vlákna.
Pthreads API. Pthreads API poskytuje štandardnú sadu funkcií na vytváranie, ukončovanie a synchronizáciu vlákien. Funkcia pthread_create() vytvára nové vlákno, pthread_exit() ho ukončuje a pthread_join() čaká na ukončenie vlákna.
Synchronizácia vlákien. Vlákna používajú mutexy a podmienkové premenné na synchronizáciu prístupu k zdieľaným zdrojom. Mutexy zabezpečujú výhradný prístup, zatiaľ čo podmienkové premenné umožňujú čakať na zmenu stavu zdieľanej premennej.
Bezpečnosť vlákien. Funkcie bezpečné pre vlákna môžu byť volané súčasne z viacerých vlákien. Funkcie nebezpečné pre vlákna by sa mali v multivláknových programoch vyhýbať. Dáta špecifické pre vlákno a lokálne úložisko vlákna umožňujú spraviť nebezpečné funkcie bezpečnými bez zmeny ich rozhraní.
8. Medziprocesová komunikácia: Zdieľanie dát a synchronizácia
Prehľad medziprocesovej komunikácie.
Mechanizmy IPC. UNIXové systémy poskytujú rôzne mechanizmy medziprocesovej komunikácie (IPC), vrátane rúr, FIFO, správových front, semaforov, zdieľanej pamäte a soketov. Tieto mechanizmy slúžia na výmenu dát a synchronizáciu procesov.
Prenos dát vs. zdieľaná pamäť. Prenos dát (rúry, FIFO, správové fronty, sokety) zahŕňa kopírovanie dát medzi procesmi, zatiaľ čo zdieľaná pamäť umožňuje priamy prístup k rovnakej oblasti pamäte. Zdieľaná pamäť je rýchlejšia, ale vyžaduje explicitnú synchronizáciu.
System V vs. POSIX IPC. System V IPC mechanizmy (správové fronty, semafory, zdieľaná pamäť) sú staršie a široko dostupné, no ich API je zložitejšie a menej konzistentné s tradičným UNIXovým modelom I/O. POSIX IPC poskytuje jednoduchšie a konzistentnejšie API, ale nie je dostupné vo všetkých UNIX implementáciách.
9. Sokety: Prepojenie cez siete
Sokety: úvod.
Sokety ako IPC. Sokety sú univerzálny mechanizmus IPC, ktorý umožňuje komunikáciu medzi procesmi na rovnakom hostiteľovi (UNIX doménové sokety) alebo medzi procesmi na rôznych hostiteľoch cez sieť (Internet doménové sokety).
Typy soketov. Existujú dva hlavné typy soketov: streamové sokety (SOCK_STREAM), ktoré poskytujú spoľahlivý, obojsmerný prúd bajtov, a datagramové sokety (SOCK_DGRAM), ktoré poskytujú nespoľahlivú, bezspojovú, správovo orientovanú komunikáciu.
Operácie so soketmi. Kľúčové systémové volania sú socket() (vytvorenie soketu), bind() (priradenie adresy), listen() (nastavenie pasívneho režimu pre streamový soket), accept() (prijatie spojenia) a connect() (pripojenie k soketu partnera).
Internet doménové sokety. Používajú IP adresy a porty na identifikáciu komunikačných koncových bodov. Funkcie getaddrinfo() a getnameinfo() slúžia na prevod medzi názvami hostiteľov a služieb a ich číselnými reprezentáciami.
10. Terminály a pseudoterminály: Interakcia s používateľom
Terminály.
Atribúty terminálu. Terminály majú množstvo atribútov, ktoré riadia ich správanie, vrátane špeciálnych znakov, príznakov a režimov I/O. Tieto atribúty je možné získať a meniť pomocou funkcií tcgetattr() a tcsetattr().
Režimy I/O terminálu. Terminály môžu pracovať v kanonickom režime (vstup po riadkoch) alebo nekanonickom režime (vstup po znakoch). Režim I/O sa riadi príznakmi terminálu.
Pseudoterminály. Pseudoterminály (pty) sú dvojice prepojených virtuálnych zariadení – master a slave. Slave zariadenie sa správa ako terminál, zatiaľ čo master zariadenie umožňuje jeho ovládanie. Pseudoterminály sa používajú v rôznych aplikáciách, napríklad v terminálových oknách alebo sieťových prihlasovacích službách.
Ovládanie terminálu. Systémové volanie ioctl() umožňuje vykonávať rôzne riadiace operácie na termináloch, vrátane nastavenia atribútov, ovládania linky a získavania veľkosti okna terminálu.
11. Bezpečnosť a schopnosti: Ochrana systému
Písanie bezpečných privilegovaných programov.
Privilegované programy. Privilegované programy majú prístup k systémovým zdrojom, ktoré nie sú dostupné bežným používateľom. Takéto programy treba písať s veľkou opatrnosťou, aby sa predišlo bezpečnostným dieram.
Najmenšie potrebné práva. Privilegované programy by mali fungovať s najmenšími právami potrebnými na vykonanie úloh. To znamená znižovať práva, keď nie sú potrebné, a trvalo ich znižovať, keď už nikdy nebudú potrebné.
Validácia vstupov. Privilegované programy musia dôkladne overovať všetky vstupy z nedôveryhodných zdrojov, vrátane argumentov príkazového riadku, premenných prostredia a dát zo súborov či sietí.
Schopnosti (Capabilities). Linuxový systém schopností rozdeľuje tradičné superužívateľské práva na samostatné jednotky nazývané schopnosti. To umožňuje procesu získať len tie práva, ktoré skutočne potrebuje, čím sa znižuje riziko škôd pri kompromitovaní programu.
12. Zdieľané knižnice: Opätovné použitie kódu a efektivita
Základy zdieľaných knižníc.
Objektové knižnice. Objektové knižnice sú súbory obsahujúce skompilovaný objektový kód pre sadu funkcií. Statické knižnice sa viažu do spustiteľného súboru pri kompilácii, zatiaľ čo zdieľané knižnice sa načítavajú za behu.
Zdieľané knižnice. Zdieľané knižnice umožňujú viacerým procesom zdieľať rovnakú kópiu knižničného kódu v pamäti, čím šetria miesto na disku aj RAM. Tiež umožňujú aktualizácie knižníc bez potreby znovu prelinkovať programy.
Kód nezávislý od pozície. Zdieľané knižnice musia byť skompilované ako kód nezávislý od pozície (PIC), čo umožňuje ich načítanie na ľubovoľnú adresu v pamäti.
Dynamické linkovanie. Dynamický linker je zodpovedný za načítanie zdieľaných knižníc za behu a riešenie symbolických odkazov. Premenná prostredia LD_LIBRARY_PATH môže špecifikovať ďalšie adresáre, kde má linker hľadať knižnice.
Viditeľnosť symbolov. Zdieľané knižnice by mali exportovať len symboly, ktoré sú súčasťou ich verejného API. Na kontrolu viditeľnosti symbolov a vytváranie verziovaných symbolov sa používajú verziovacie skripty.
Ľudia tiež čítajú
Časté otázky
What's The Linux Programming Interface about?
- Comprehensive Guide: The Linux Programming Interface by Michael Kerrisk is a detailed handbook on Linux and UNIX system programming, covering system calls, library functions, and the programming interface used by nearly every application on these systems.
- Focus on Standards: The book emphasizes UNIX standards (POSIX.1-2001/SUSv3 and POSIX.1-2008/SUSv4), making it valuable for programmers working on various UNIX platforms.
- Practical Examples: It includes over 200 example programs, 88 tables, and 115 diagrams to illustrate concepts, making it easier for readers to grasp complex topics.
Why should I read The Linux Programming Interface?
- Authoritative Resource: Written by Michael Kerrisk, a recognized expert in Linux programming, this book is considered the definitive guide in the field. It is praised for its clarity and depth.
- Skill Development: The book is designed for programmers and software designers looking to build applications for Linux or other UNIX systems, enhancing their system programming skills.
- Comprehensive Coverage: It covers a wide range of topics, from basic file I/O to advanced features like multithreading and interprocess communication, making it suitable for both beginners and experienced developers.
What are the key takeaways of The Linux Programming Interface?
- Understanding System Calls: Readers will learn how to effectively use system calls and library functions, which are crucial for interacting with the Linux kernel.
- File I/O Model: The book introduces the universal I/O model, explaining how the same system calls are used for all types of files, including devices and sockets.
- Error Handling: It emphasizes the importance of checking return values from system calls and library functions, teaching readers how to handle errors effectively.
What are the best quotes from The Linux Programming Interface and what do they mean?
- Comprehensive Coverage: “The Linux Programming Interface is the most comprehensive single-volume work on the Linux and UNIX programming interface.” This highlights the book's extensive coverage and authority in the field.
- Depth of Content: “You’ll find descriptions of over 500 system calls and library functions.” This emphasizes the book's depth, indicating a thorough exploration of the programming interface.
- Transferable Knowledge: “The emphasis on UNIX standards makes it equally valuable to programmers working on other UNIX platforms.” This suggests that the knowledge gained is applicable across different UNIX systems.
How does The Linux Programming Interface explain system calls?
- Definition of System Calls: System calls are controlled entry points into the kernel that allow processes to request services, such as file operations or process management.
- Kernel Interaction: They enable user programs to interact with the kernel, which manages system resources and enforces security and access controls.
- Performance Overhead: Understanding the overhead associated with system calls is crucial for writing efficient programs, as they involve switching from user mode to kernel mode.
What is the universal I/O model discussed in The Linux Programming Interface?
- Unified System Calls: The universal I/O model states that the same system calls (open(), read(), write(), close()) are used for all types of files, including regular files, pipes, and sockets.
- Simplified Programming: This model simplifies the programming model, allowing developers to write code that works across different file types without needing to handle each type differently.
- Practical Examples: The book provides examples that illustrate how to use these system calls effectively, reinforcing the concept of universality in file I/O.
How does The Linux Programming Interface address error handling?
- Importance of Error Checking: The book stresses that every system call and library function should have its return value checked to ensure successful execution.
- Using errno: It explains how to use the global variable
errnoto diagnose errors, providing functions likeperror()andstrerror()for user-friendly error messages. - Example Implementation: The book includes practical examples demonstrating how to handle errors effectively, which is essential for robust programming.
What are the differences between static and shared libraries as explained in The Linux Programming Interface?
- Static Libraries: These are archives of object code that are linked into an executable at compile time, resulting in larger binaries and requiring recompilation for updates.
- Shared Libraries: These are linked at runtime, allowing multiple programs to share a single copy of the library in memory, which saves space and simplifies updates.
- Advantages of Shared Libraries: The book discusses how shared libraries facilitate easier updates and reduce memory usage, making them preferable in many scenarios.
How does The Linux Programming Interface explain process management?
- Definition of a Process: A process is defined as an instance of an executing program, with its own memory space and resources managed by the kernel.
- Process Creation: The book details how processes are created using the fork() system call, which duplicates the parent process, and how they can execute new programs using execve().
- Process Management: It covers how the kernel manages processes, including scheduling, resource allocation, and termination, providing a comprehensive understanding of process behavior.
What is the significance of the /proc file system in The Linux Programming Interface?
- Virtual File System: The /proc file system is a virtual file system that provides an interface to kernel data structures, allowing users to view and modify system attributes.
- Process Information: It contains directories for each running process, providing insights into process states and resource usage, which is invaluable for debugging and monitoring.
- Kernel Interaction: The book explains how to interact with /proc files to gather system information, making it a practical tool for system programmers.
How does The Linux Programming Interface explain interprocess communication (IPC)?
- Diverse IPC Methods: The book covers various IPC methods, including pipes, message queues, and shared memory. Each method is explained with examples, allowing readers to understand their use cases and implementation.
- Synchronization Techniques: It also discusses synchronization mechanisms, such as semaphores and mutexes, which are crucial for coordinating access to shared resources.
- Practical Examples: The author provides practical examples of IPC in action, demonstrating how to implement these techniques in real-world applications.
What are the threading concepts discussed in The Linux Programming Interface?
- POSIX Threads: The book provides a comprehensive overview of POSIX threads, including thread creation, termination, and synchronization. This is essential for developing multithreaded applications in Linux.
- Thread Safety: It emphasizes the importance of writing thread-safe code, discussing techniques to manage shared resources and avoid race conditions.
- Cancellation and Cleanup: The book covers thread cancellation and the use of cleanup handlers, which are important for managing resources in multithreaded programs.