You are previewing Linux. Programowanie systemowe. Wydanie II.
O'Reilly logo
Linux. Programowanie systemowe. Wydanie II

Book Description

Przewodnik po jądrze sytemu Linux! Jądro systemu Linux to jeden z największych projektów rozwijanych przez ogromną społeczność. Setki wolontariuszy dniami i nocami rozwijają najważniejszy element tego systemu operacyjnego. I robią to naprawdę skutecznie! Każde kolejne wydanie Linuksa zawiera dziesiątki nowinek oraz ulepszeń - jest coraz szybsze, bezpieczniejsze i po prostu lepsze. Jednak początkujący programiści mogą mieć problemy z wykorzystaniem usług dostarczanych przez kernel. Masz obawy, że nie odnajdziesz się w gąszczu możliwości współczesnego jądra systemu Linux? Ta książka rozwieje je w mig. Jest to wyjątkowa pozycja na rynku wydawniczym, zgłębiająca właśnie te tajemnice. W trakcie lektury nauczysz się tworzyć niskopoziomowe oprogramowanie, które będzie się komunikowało bezpośrednio z jądrem systemu. Operacje wejścia i wyjścia, strumienie, zdarzenia, procesy to tylko część elementów, które błyskawicznie opanujesz. Ponadto nauczysz się zarządzać katalogami i plikami oraz poznasz koncepcję sygnałów. Książka ta jest niezastąpionym źródłem informacji dla wszystkich programistów pracujących z jądrem Linuksa. Docenisz tę lekturę! Poznaj metody zarządzania procesami zastosowanie sygnałów zaawansowane interfejsy wejścia i wyjścia jądro systemu od podszewki Poznaj jądro systemu od podszewki! Robert Love - od wielu lat jest użytkownikiem i współtwórcą systemu Linux. Rozwija środowisko graficzne GNOME oraz jądro systemu. Pracuje jako projektant oprogramowania w firmie Google, był też członkiem zespołu projektującego system operacyjny Android. Jest autorem licznych książek poświęconych programowaniu w systemie Linux.

Table of Contents

  1. Linux: Programowanie Systemowe
  2. Przedmowa
  3. Wstęp
    1. Przyjęte założenia, dotyczące odbiorców książki
    2. Zawartość książki
    3. Wersje uwzględnione w tej książce
    4. Użyte konwencje
    5. Używanie przykładowych kodów
    6. Podziękowania
  4. 1. Wprowadzenie — podstawowe pojęcia
    1. Programowanie systemowe
      1. Dlaczego warto uczyć się programowania systemowego?
      2. Kamienie węgielne programowania systemowego
      3. Funkcje systemowe
        1. Wywoływanie funkcji systemowych
      4. Biblioteka języka C
      5. Kompilator języka C
    2. API i ABI
      1. API
      2. ABI
    3. Standardy
      1. Historia POSIX oraz SUS
      2. Standardy języka C
      3. Linux i standardy
      4. Książka i standardy
    4. Pojęcia dotyczące programowania w Linuksie
      1. Pliki i system plików
        1. Pliki zwykłe
        2. Katalogi i dowiązania
        3. Dowiązania twarde
        4. Dowiązania symboliczne
        5. Pliki specjalne
        6. Systemy plików i przestrzenie nazw
      2. Procesy
        1. Wątki
        2. Hierarchia procesów
      3. Użytkownicy i grupy
      4. Uprawnienia
      5. Sygnały
      6. Komunikacja międzyprocesowa
      7. Pliki nagłówkowe
      8. Obsługa błędów
    5. Początek programowania systemowego
  5. 2. Plikowe operacje wejścia i wyjścia
    1. Otwieranie plików
      1. Funkcja systemowa open()
        1. Znaczniki w funkcji open()
      2. Właściciele nowych plików
      3. Uprawnienia nowych plików
      4. Funkcja creat()
      5. Wartości zwracane i kody błędów
    2. Czytanie z pliku przy użyciu funkcji read()
      1. Wartości zwracane
      2. Czytanie wszystkich bajtów
      3. Odczyty nieblokujące
      4. Inne wartości błędów
      5. Ograniczenia rozmiaru dla funkcji read()
    3. Pisanie za pomocą funkcji write()
      1. Zapisy częściowe
      2. Tryb dopisywania
      3. Zapisy nieblokujące
      4. Inne kody błędów
      5. Ograniczenia rozmiaru dla funkcji write()
      6. Sposób działania funkcji write()
    4. Zsynchronizowane operacje wejścia i wyjścia
      1. Funkcje fsync() i fdatasync()
        1. Wartości zwracane i kody błędów
      2. Funkcja sync()
      3. Znacznik O_SYNC
      4. Znaczniki O_DSYNC i O_RSYNC
    5. Bezpośrednie operacje wejścia i wyjścia
    6. Zamykanie plików
      1. Kody błędów
    7. Szukanie za pomocą funkcji lseek()
      1. Szukanie poza końcem pliku
      2. Kody błędów
      3. Ograniczenia
    8. Odczyty i zapisy pozycyjne
      1. Kody błędów
    9. Obcinanie plików
    10. Zwielokrotnione operacje wejścia i wyjścia
      1. Funkcja select()
        1. Wartości powrotu oraz kody błędów
        2. Przykład użycia funkcji select()
        3. Przenośny sposób wstrzymania wykonania aplikacji za pomocą funkcji select()
        4. Funkcja pselect()
      2. Funkcja poll()
        1. Wartości powrotu oraz kody błędów
        2. Przykład użycia funkcji poll()
        3. Funkcja ppoll()
      3. Porównanie funkcji poll() i select()
    11. Organizacja wewnętrzna jądra
      1. Wirtualny system plików
      2. Bufor stron
      3. Opóźniony zapis stron
    12. Zakończenie
  6. 3. Buforowane operacje wejścia i wyjścia
    1. Operacje wejścia i wyjścia buforowane w przestrzeni użytkownika
      1. Rozmiar bloku
    2. Typowe operacje wejścia i wyjścia
      1. Wskaźniki do plików
    3. Otwieranie plików
      1. Tryby
    4. Otwieranie strumienia poprzez deskryptor pliku
    5. Zamykanie strumieni
      1. Zamykanie wszystkich strumieni
    6. Czytanie ze strumienia
      1. Czytanie pojedynczego znaku
        1. Wycofywanie znaku
      2. Czytanie całego wiersza
        1. Czytanie dowolnych łańcuchów
      3. Czytanie danych binarnych
    7. Pisanie do strumienia
      1. Zapisywanie pojedynczego znaku
      2. Zapisywanie łańcucha znaków
      3. Zapisywanie danych binarnych
    8. Przykładowy program używający buforowanych operacji wejścia i wyjścia
    9. Szukanie w strumieniu
      1. Otrzymywanie informacji o aktualnym położeniu w strumieniu
    10. Opróżnianie strumienia
    11. Błędy i koniec pliku
    12. Otrzymywanie skojarzonego deskryptora pliku
    13. Parametry buforowania
    14. Bezpieczeństwo wątków
      1. Nieautomatyczne blokowanie plików
      2. Nieblokowane operacje na strumieniu
    15. Krytyczna analiza biblioteki typowych operacji wejścia i wyjścia
    16. Zakończenie
  7. 4. Zaawansowane operacje plikowe wejścia i wyjścia
    1. Rozproszone operacje wejścia i wyjścia
      1. Funkcje readv() i writev()
        1. Wartości powrotne
        2. Przykład użycia funkcji writev()
        3. Przykład użycia funkcji readv()
        4. Implementacja
    2. Odpytywanie zdarzeń
      1. Tworzenie nowego egzemplarza interfejsu odpytywania zdarzeń
      2. Sterowanie działaniem interfejsu odpytywania zdarzeń
      3. Oczekiwanie na zdarzenie w interfejsie odpytywania zdarzeń
      4. Zdarzenia przełączane zboczem a zdarzenia przełączane poziomem
    3. Odwzorowywanie plików w pamięci
      1. Funkcja mmap()
        1. Rozmiar strony
        2. Wartości powrotne i kody błędów
        3. Dodatkowe sygnały
      2. Funkcja munmap()
      3. Przykład odwzorowania w pamięci
      4. Zalety używania funkcji mmap()
      5. Wady używania funkcji mmap()
      6. Zmiana rozmiaru odwzorowania
        1. Wartości powrotne i kody błędów
      7. Zmiana uprawnień odwzorowania
        1. Wartości powrotne i kody błędów
      8. Synchronizacja odwzorowanego pliku
        1. Wartości powrotne i kody błędów
      9. Dostarczanie porad dotyczących odwzorowania w pamięci
        1. Wartości powrotne i kody błędów
    4. Porady dla standardowych operacji plikowych wejścia i wyjścia
      1. Funkcja systemowa posix_fadvise()
        1. Wartości powrotne i kody błędów
      2. Funkcja systemowa readahead()
        1. Wartości powrotne i kody błędów
      3. Porada jest tania
    5. Operacje zsynchronizowane, synchroniczne i asynchroniczne
      1. Asynchroniczne operacje wejścia i wyjścia
    6. Zarządcy operacji wejścia i wyjścia oraz wydajność operacji wejścia i wyjścia
      1. Adresowanie dysku
      2. Działanie zarządcy operacji wejścia i wyjścia
      3. Wspomaganie odczytów
        1. Zarządca z terminem nieprzekraczalnym
        2. Zarządca przewidujący
        3. Zarządca ze sprawiedliwym szeregowaniem
        4. Zarządca niesortujący
      4. Wybór i konfiguracja zarządcy operacji wejścia i wyjścia
      5. Optymalizowanie wydajności operacji wejścia i wyjścia
        1. Szeregowanie operacji wejścia i wyjścia w przestrzeni użytkownika
          1. Sortowanie wg ścieżki
          2. Sortowanie wg numeru i-węzła.
          3. Sortowanie wg numeru fizycznego bloku
    7. Zakończenie
  8. 5. Zarządzanie procesami
    1. Programy, procesy i wątki
    2. Identyfikator procesu
      1. Przydział identyfikatorów procesów
      2. Hierarchia procesów
      3. Typ pid_t
      4. Otrzymywanie identyfikatora procesu oraz identyfikatora procesu rodzicielskiego
    3. Uruchamianie nowego procesu
      1. Rodzina funkcji exec
        1. Pozostałe elementy grupy funkcji exec
        2. Kody błędów
      2. Funkcja systemowa fork()
        1. Kopiowanie podczas zapisu
        2. Funkcja vfork()
    4. Zakończenie procesu
      1. Inne sposoby na zakończenie procesu
      2. Funkcja atexit()
      3. Funkcja on_exit()
      4. Sygnał SIGCHLD
    5. Oczekiwanie na zakończone procesy potomka
      1. Oczekiwanie na określony proces
      2. Jeszcze wszechstronniejsza funkcja oczekiwania
      3. BSD wkracza do akcji: funkcje wait3() i wait4()
      4. Uruchamianie i oczekiwanie na nowy proces
      5. Procesy zombie
    6. Użytkownicy i grupy
      1. Rzeczywiste, efektywne oraz zapisane identyfikatory użytkownika i grupy
      2. Zmiana rzeczywistego lub zapisanego identyfikatora dla użytkownika lub grupy
      3. Zmiana efektywnego identyfikatora dla użytkownika lub grupy
      4. Zmiana identyfikatora dla użytkownika lub grupy w wersji BSD
      5. Zmiana identyfikatora dla użytkownika lub grupy w wersji HP-UX
      6. Zalecane modyfikacje identyfikatorów użytkownika i grupy
      7. Wsparcie dla zapisanych identyfikatorów użytkownika
      8. Otrzymywanie identyfikatorów użytkownika i grupy
    7. Grupy sesji i procesów
      1. Funkcje systemowe do obsługi sesji
      2. Funkcje systemowe do obsługi grup procesów
      3. Przestarzałe funkcje do obsługi grupy procesów
    8. Demony
    9. Zakończenie
  9. 6. Zaawansowane zarządzanie procesami
    1. Szeregowanie procesów
      1. Przedziały czasowe
      2. Procesy związane z wejściem i wyjściem a procesy związane z procesorem
      3. Szeregowanie z wywłaszczaniem
    2. Completely Fair Scheduler
    3. Udostępnianie czasu procesora
      1. Prawidłowe sposoby użycia sched_yield()
    4. Priorytety procesu
      1. nice()
      2. getpriority() i setpriority()
      3. Priorytety wejścia i wyjścia
    5. Wiązanie procesów do konkretnego procesora
      1. sched_getaffinity() i sched_setaffinity()
    6. Systemy czasu rzeczywistego
      1. Systemy ścisłego oraz zwykłego czasu rzeczywistego
      2. Opóźnienie, rozsynchronizowanie oraz parametry graniczne
      3. Obsługa czasu rzeczywistego przez system Linux
      4. Linuksowe strategie szeregowania i ustalania priorytetów
        1. Strategia FIFO (first in, first out)
        2. Strategia cykliczna
        3. Strategia zwykła
        4. Strategia szeregowania wsadowego
        5. Ustalanie strategii szeregowania dla systemu Linux
          1. Kody błędów
      5. Ustawianie parametrów szeregowania
        1. Kody błędów
        2. Określanie zakresu poprawnych priorytetów
      6. sched_rr_get_interval()
        1. Kody błędu
      7. Środki ostrożności przy pracy z procesami czasu rzeczywistego
      8. Determinizm
        1. Wcześniejsze zapisywanie danych oraz blokowanie pamięci
        2. Wiązanie do procesora a procesy czasu rzeczywistego
    7. Ograniczenia zasobów systemowych
      1. Ograniczenia
        1. Ograniczenia domyślne
      2. Ustawianie i odczytywanie ograniczeń
        1. Kody błędów
  10. 7. Wątkowość
    1. Binaria, procesy i wątki
    2. Wielowątkowość
      1. Koszty wielowątkowości
      2. Alternatywy dla wielowątkowości
    3. Modele wątkowości
      1. Wątkowość na poziomie użytkownika
      2. Wątkowość mieszana
      3. Współprogramy i włókna
    4. Wzorce wątkowości
      1. Wątkowość thread-per-connection
      2. Wątkowość sterowana zdarzeniami
    5. Współbieżność, równoległość i wyścigi
      1. Sytuacje wyścigów
        1. Przykłady wyścigów ze świata rzeczywistego
    6. Synchronizacja
      1. Muteksy
      2. Zakleszczenia
        1. Unikanie zakleszczeń
    7. Standard Pthreads
      1. Implementacje wątkowości w Linuksie
      2. Interfejs programistyczny dla standardu Pthreads
      3. Konsolidowanie implementacji Pthreads
      4. Tworzenie wątków
      5. Identyfikatory wątków
        1. Porównywanie identyfikatorów wątków
      6. Kończenie wątków
        1. Zakończenie samego siebie
        2. Zakończenie innych wątków
      7. Łączenie i odłączanie wątków
        1. Łączenie wątków
        2. Odłączanie wątków
      8. Przykład wątkowości
      9. Muteksy standardu Pthreads
        1. Inicjalizowanie muteksów
        2. Blokowanie muteksów
        3. Odblokowywanie muteksów
        4. Przykład użycia muteksu
    8. Dalsze zdobywanie wiedzy
  11. 8. Zarządzanie plikami i katalogami
    1. Pliki i ich metadane
      1. Rodzina funkcji stat
      2. Uprawnienia
      3. Prawa własności
      4. Atrybuty rozszerzone
        1. Klucze i wartości
        2. Przestrzenie nazw dla atrybutów rozszerzonych
      5. Operacje dla atrybutów rozszerzonych
        1. Odczytywanie atrybutu rozszerzonego
        2. Ustawianie atrybutu rozszerzonego
        3. Wyświetlanie atrybutów rozszerzonych dla pliku
        4. Usuwanie atrybutu rozszerzonego
    2. Katalogi
      1. Aktualny katalog roboczy
        1. Odczytywanie aktualnego katalogu roboczego
        2. Zmiana aktualnego katalogu roboczego
      2. Tworzenie katalogów
      3. Usuwanie katalogów
      4. Odczytywanie zawartości katalogu
        1. Czytanie ze strumienia katalogu
        2. Zamykanie strumienia katalogu
        3. Funkcje systemowe służące do odczytywania zawartości katalogu
    3. Dowiązania
      1. Dowiązania twarde
      2. Dowiązania symboliczne
      3. Usuwanie elementów z systemu plików
    4. Kopiowanie i przenoszenie plików
      1. Kopiowanie
      2. Przenoszenie
    5. Węzły urządzeń
      1. Specjalne węzły urządzeń
      2. Generator liczb losowych
    6. Komunikacja poza kolejką
    7. Śledzenie zdarzeń związanych z plikami
      1. Inicjalizacja interfejsu inotify
      2. Elementy obserwowane
        1. Dodawanie nowego elementu obserwowanego
        2. Maska elementu obserwowanego
      3. Zdarzenia interfejsu inotify
        1. Odczytywanie zdarzeń inotify
        2. Zaawansowane zdarzenia inotify
        3. Łączenie zdarzeń przenoszenia
      4. Zaawansowane opcje obserwowania
      5. Usuwanie elementu obserwowanego z interfejsu inotify
      6. Otrzymywanie rozmiaru kolejki zdarzeń
      7. Usuwanie egzemplarza interfejsu inotify
  12. 9. Zarządzanie pamięcią
    1. Przestrzeń adresowa procesu
      1. Strony i stronicowanie
        1. Współdzielenie i kopiowanie podczas zapisu
      2. Regiony pamięci
    2. Przydzielanie pamięci dynamicznej
      1. Przydzielanie pamięci dla tablic
      2. Zmiana wielkości obszaru przydzielonej pamięci
      3. Zwalnianie pamięci dynamicznej
      4. Wyrównanie
        1. Przydzielanie pamięci wyrównanej
        2. Inne zagadnienia związane z wyrównaniem
    3. Zarządzanie segmentem danych
    4. Anonimowe odwzorowania w pamięci
      1. Tworzenie anonimowych odwzorowań w pamięci
      2. Odwzorowanie pliku /dev/zero
    5. Zaawansowane operacje przydziału pamięci
      1. Dokładne dostrajanie przy użyciu funkcji malloc_usable_size() oraz malloc_trim()
    6. Uruchamianie programów używających systemu przydzielania pamięci
      1. Otrzymywanie danych statystycznych
    7. Przydziały pamięci wykorzystujące stos
      1. Powielanie łańcuchów znakowych na stosie
      2. Tablice o zmiennej długości
    8. Wybór mechanizmu przydzielania pamięci
    9. Operacje na pamięci
      1. Ustawianie wartości bajtów
      2. Porównywanie bajtów
      3. Przenoszenie bajtów
      4. Wyszukiwanie bajtów
      5. Manipulowanie bajtami
    10. Blokowanie pamięci
      1. Blokowanie fragmentu przestrzeni adresowej
      2. Blokowanie całej przestrzeni adresowej
      3. Odblokowywanie pamięci
      4. Ograniczenia blokowania
      5. Czy strona znajduje się w pamięci fizycznej?
    11. Przydział oportunistyczny
      1. Przekroczenie zakresu zatwierdzenia oraz stan braku pamięci (OOM)
  13. 10. Sygnały
    1. Koncepcja sygnałów
      1. Identyfikatory sygnałów
      2. Sygnały wspierane przez system Linux
    2. Podstawowe zarządzanie sygnałami
      1. Oczekiwanie na dowolny sygnał
      2. Przykłady
      3. Uruchomienie i dziedziczenie
      4. Odwzorowanie numerów sygnałów na łańcuchy znakowe
    3. Wysyłanie sygnału
      1. Uprawnienia
      2. Przykłady
      3. Wysyłanie sygnału do samego siebie
      4. Wysyłanie sygnału do całej grupy procesów
    4. Współużywalność
      1. Funkcje, dla których współużywalność jest zagwarantowana
    5. Zbiory sygnałów
      1. Inne funkcje obsługujące zbiory sygnałów
    6. Blokowanie sygnałów
      1. Odzyskiwanie oczekujących sygnałów
      2. Oczekiwanie na zbiór sygnałów
    7. Zaawansowane zarządzanie sygnałami
      1. Struktura siginfo_t
      2. Wspaniały świat pola si_code
    8. Wysyłanie sygnału z wykorzystaniem pola użytkowego
      1. Przykład wykorzystania pola użytkowego
    9. Ułomność systemu Unix?
  14. 11. Czas
    1. Struktury danych reprezentujące czas
      1. Reprezentacja pierwotna
      2. Następna wersja — dokładność na poziomie mikrosekund
      3. Kolejna, lepsza wersja — dokładność na poziomie nanosekund
      4. Wyłuskiwanie składników czasu
      5. Typ danych dla czasu procesu
    2. Zegary POSIX
      1. Rozdzielczość źródła czasu
    3. Pobieranie aktualnego czasu
      1. Lepszy interfejs
      2. Interfejs zaawansowany
      3. Pobieranie czasu procesu
    4. Ustawianie aktualnego czasu
      1. Precyzyjne ustawianie czasu
      2. Zaawansowany interfejs ustawiania czasu
    5. Konwersje czasu
    6. Dostrajanie zegara systemowego
    7. Stan uśpienia i oczekiwania
      1. Obsługa stanu uśpienia z dokładnością do mikrosekund
      2. Obsługa stanu uśpienia z dokładnością do nanosekund
      3. Zaawansowane zarządzanie stanem uśpienia
      4. Przenośny sposób wprowadzania w stan uśpienia
      5. Przepełnienia
      6. Alternatywy stanu uśpienia
    8. Liczniki
      1. Proste alarmy
      2. Liczniki interwałowe
      3. Liczniki zaawansowane
        1. Tworzenie licznika
        2. Inicjalizowanie licznika
        3. Odczytywanie czasu wygaśnięcia licznika
        4. Odczytywanie wartości przepełnienia licznika
        5. Usuwanie licznika
  15. A. Rozszerzenia kompilatora GCC dla języka C
    1. GNU C
    2. Funkcje wplatane (inline)
    3. Zapobieganie wplataniu funkcji
    4. Funkcje czyste (pure)
    5. Funkcje stałe
    6. Funkcje, które nie wracają do procedury wywołującej
    7. Funkcje przydzielające pamięć
    8. Wymuszanie sprawdzania wartości powrotnej dla procedur wywołujących
    9. Oznaczanie funkcji niezalecanych
    10. Oznaczanie funkcji używanych
    11. Oznaczanie funkcji lub parametrów nieużywanych
    12. Pakowanie struktury
    13. Zwiększanie wartości wyrównania dla zmiennej
    14. Umieszczanie zmiennych globalnych w rejestrach
    15. Optymalizacja gałęzi kodu
    16. Uzyskiwanie typu dla wyrażenia
    17. Uzyskiwanie wielkości wyrównania dla danego typu
    18. Pozycja elementu w strukturze
    19. Uzyskiwanie powrotnego adresu funkcji
    20. Zakresy funkcji case
    21. Arytmetyka wskaźników do funkcji oraz wskaźników void
    22. Więcej przenośności i elegancji za jednym razem
  16. B. Bibliografia
    1. Programowanie w języku C
    2. Programowanie w Linuksie
    3. Jądro Linuksa
    4. Projektowanie systemu operacyjnego
  17. C. O autorze
  18. Indeks
  19. Kolofon
  20. Copyright