Systemy operacyjne ZSOP. Wykład 2

Michał Goliński

2017-10-22

Wstęp

Pojęcia – przypomnienie

Ostatnio poznaliśmy główne pojęcia:

  • system operacyjny
  • proces
  • wywłaszczanie procesu
  • pamięć wirtualna
  • system plików
  • terminal
  • powłoka

Polecenia – przypomnienie

Poznaliśmy również podstawowe polecenia:

  • man, apropos, info
  • cd
  • ls
  • cp, mv, rm
  • mkdir, rmdir
  • ln
  • touch

Pytania?

Plan na dziś

  • Cykl życia procesu
  • Wejście i wyjście
  • Potoki
  • Specjalne urządzenia znakowe
  • Polecenie działające na tekście
  • Narzędzia przydatne przy pracy z potokami
  • Archiwizacja i kompresja danych

Cykl życia procesu

Diagram stanów procesu

Możliwe stany procesu można obrazowo przedstawić w postaci diagramu:

Diagram stanu procesu
Diagram stanu procesu

Deskryptor procesu

System operacyjny przechowuje informacje o procesach w tzw. deskryptorach procesów. W szczególności przechowywane są:

  • numer (identyfikator) procesu
  • numer procesu rodzica
  • zużyty czas procesora
  • stan pamięci wirtualnej (tablica stron)
  • adres aktualnie wykonywanej instrukcji
  • otwarte pliki

Deskryptor procesu cd.

W Linuksie deskryptor procesu opisuje struktura task_struct w pliku include/linux/sched.h.

Wiele informacji o działającym procesie można znaleźć w katalogu /proc/<numer procesu>.

Przerwania

Przerwaniem (interrupt) nazywamy szczególny mechanizm w procesorach sygnalizujący nadejście jakiegoś ważnego wydarzenia, które trzeba obsłużyć. Przerwanie powoduje zawieszenie wykonywania aktualnego kodu i przejście do tzw. procedury obsługi przerwania.

Przerwania cd.

Przerwanie może być wynikiem zewnętrznego zdarzenia sprzętowego (np. wciśnięcie klawisza) lub wewnętrznego (wyjątku), sygnalizowanego najczęściej przez procesor (np. dzielenie przez zero). Przerwanie można też wywołać programowo – to jest z grubsza mechanizm przez który program w przestrzeni użytkownika porozumiewa się z systemem operacyjnym, czyli wywołuje funkcje systemowe.

Wywłaszczanie procesów

Aby móc wywłaszczyć proces (czyli odłączyć proces od procesora) i wykonywać inne niezbędne czynności, systemy operacyjne korzystają z tzw. przerwania zegarowego, czyli przerwania generowanego w ściśle określonych momentach czasu. W przeszłości działo się to ze ściśle określoną częstotliwością (np 100 Hz). Nowoczesne jądra ustawiają przerwania zegarowe w nieregularnych odstępach czasu, tylko wtedy gdy to potrzebne.

Wywłaszczanie procesów cd.

Jeżeli w czasie obsługi przerwania (zegarowego lub innego) system operacyjny stwierdzi, że obecnie wykonywany proces działa zbyt długo i inny proces zasługuje na procesor (decyzję tę podejmuje scheduler), to adres bieżącej instrukcji jest zapisywany, a do procesora ładowany jest adres instrukcji innego procesu, który zacznie działać po zakończeniu obsługi przerwania.

Priorytet procesu

Na pracę schedulera można wpływać poprzez przeróżne parametry. Głównym z nich jest priorytet procesu. W Linuksie priorytem manipulujemy ustawiając inny parametr: nice w zakresie od -19 do +20. Proces z większym nice jest miły dla innych, więc ma niższy priorytet. Ustawienie ujemnej wartości nice wymaga uprawnień administratora.

Z poziomu linii poleceń nice dla procesu można ustawiać i zmieniać poleceniami nice i renice.

scheduler – algorytmy

scheduler

Aby zobaczyć z jakimi problemami zmagają się twórcy systemów operacyjnych zastanowimy się teraz jak mógłby działać scheduler i zobaczymy jak działa scheduler CFS w Linuksie.

Przypomnijmy: scheduler odpowiada za wybór procesu, który będzie wykonywany na procesorze.

Kryteria

Dobry scheduler:

  • podejmuje szybko decyzję (mała złożoność obliczeniowa)
  • dba o dobre wykorzystanie procesora
  • dba aby procesy, na których bardziej zależy użytkownikowi (interaktywne) dostawały procesor gdy tylko tego potrzebują
  • dba aby każdy proces w końcu dostał procesor (nie został zagłodzony)

Okresy aktywności

Większość procesów w systemie operacyjnym na zmianę oczekuje na dane i chce je przetwarzać. Dobry scheduler powinien dostosowywać się do charakteru procesu – czy prowadzi on dużo obliczeń (i prawdopodobnie może trochę poczekać na procesor), czy raczej niewiele w krótkich seriach (co sugeruje interakcję z użytkownikiem i koniecznośc natychmiastowego dostępu do procesora).

FCFS

First come, first served – procesor przydzielamy kolejno procesom w kolejności zgłaszania gotowości. Nie wywłaszczamy procesów, jedynie odbieramy im procesor gdy np. oczekują na wejście/wyjście. W momencie zakończenia operacji wejścia/wyjścia proces trafia do kolejki oczekujących na procesor.

Algorytm używany przy kasie supermarkecie.

Zalety i wady

Zalety: prostota

Wady:

  • wszyscy czekają na procesy zajmujące na długo procesor
  • Po zwolnieniu procesora będzie krótka chwila aktywności i prawdopodobnie bezczynność

SJF

Shortest job first – pierwszeństwo mają procesy, które szybciej skończą swoje aktualne zadanie. Mamy dwie możliwości: albo nie wywłaszczamy procesów, albo wywłaszczany, jeśli w kolejce znajdzie się proces, który szybciej skończy.

Zalety i wady

Zalety: algorytm ten minimalizuje (w teorii) średni czas oczekiwania na zakończenie wszystkich procesów

Główna wada: jak określić, kiedy proces zakończy działanie. Istnieją pewne heurystyki na podstawie przeszłych zachowań procesu, ale to tylko heurystyki.

Według priorytetu

Procesy dzielimy na klasy w zależności od priorytetu. Wyższy priorytet ma pierwszeństwo, w ramach priorytetu stosujemy np. FCFS.

Zalety i wady

Wada: Niski priorytet grozi zagłodzeniem procesu. Recepta: sztucznie podnosimy priorytet w miarę oczekiwania.

Zaleta: Ważne zadania mogą być wykonane szybciej.

RR

Round-robin: Procesy trafiają do kolejki. Każdy proces otrzymuje kwant czasu procesora, po czym jest wywłaszczany i trafia na koniec kolejki.

Zalety i wady

Zalety: prostota

Wady: Ciągłe przełączanie procesora między procesami, co marnuje czas.

CFS

Linux od wersji 2.6.23 żywa nowatorskiego schedulera CFS – Completely Fair Scheduler. Dla każdego procesu przechowywany jest wirtualny czas działania, który z grubsza odpowiada czasowi procesora, który ten proces wykorzystał. W każdym momencie do wykonywania wybierany jest proces, który ma najmniejszy czas wirtualny (a więc był niesprawiedliwie traktowany). Informacja o procesach jest przechowywana w drzewie czerwono-czarnym (samobalansującym drzewie binarnym).

CFS cd.

Powyższy system wygląda na sprawiedliwy, gdy nie ma nowych procesów. Nowy proces miałby zerowy czas działania i przez to nieuprawnioną przewagę. Aby temu zapobiec w praktyce w momencie dokładania nowego elementu do drzewa jego wirtualny czas działania jest sztucznie zwiększany w zależności od najmniejszej wartości czasu w drzewie. W praktyce daje to niezłe rezultaty.

Wejście i wyjście procesu

Standardowe wejście i wyjście

W momencie stworzenia nowego procesu, ma on z reguły otwarte 3 kanały komunikacji ze światem:

  • standardowe wejście (stdin)
  • standardowe wyjście (stdout)
  • standardowe wyjście błędów (stderr)

Standardowe wejście i wyjście cd.

W przypadku otwarcia programu w powłoce, jeżeli nie podejmiemy kroków by temu zapobiec, w stdin pojawiają się znaki wpisywane na klawiaturze, a stdout i stderr wypisywane są a ekranie. Normalne procedury wejścia i wyjścia w językach programowania używają stdin i stdout.

stdout a stderr

Dwa kanały wyjściowe mają za zadanie oddzielić zwykłe komunikaty działającej aplikacji (np. wynik obliczeń) od ostrzeżeń i błędów dla użytkownika. Co prawda normalnie wszystko jest wymieszane na ekranie, ale strumienie te można bez problemu rozdzielić.

Ponadto stdout jest z powodów wydajnościowych buforowany, a stderr z reguły nie. Dzięki temu komunikaty diagnostyczne mogą być natychmiast wyświetlane bez potrzeby opróżniania bufora przez programistę.

Przekierowanie z/do pliku

Powłoka pozwala użytkownikowi zażądać, aby wejście do programu było wczytywane z pliku, a nie z klawiatury. Podobnie wyjście (stdout, stderr) może być kierowane do pliku zamiast na ekran.

$ polecenie <plik_wejściowy
$ polecenie >plik_wyjściowy
$ polecenie <plik_wejściowy >plik_wyjściowy
$ polecenie 2>plik_wyjściowy_błędów

Przekierowanie z/do pliku cd.

Aby połączyć stderr z stdout używamy następującej konstrukcji:

$ polecenie 2>&1

Aby zapisać zarówno stderr jak i stdout do tego samego pliku można użyć konstrukcji:

$ polecenie &>plik_wyjściowy

Urządzenia znakowe

Idea

Urządzenia znakowe to specjalne pliki, które pozwalają na wydobywanie lub przesyłanie danych do jądra. Niektóre z nich są bardzo przydatne w kontekście przekierowań. Przedstawimy najważniejsze urządzenia znakowe.

/dev/mem

Urządzenie /dev/mem daje dostęp do zawartości pamięci fizycznej. Dostępne tylko z uprawnieniami administratora. Zmiany, a także próby odczytania niektórych rejonów pamięci) mogą mieć katastrofalne skutki dla systemu.

/dev/null

Bardzo przydatne urządzenie. Odczyt zawsze daje koniec pliku. Zapis zawsze kończy się powodzeniem a zapisane dane są po prostu kasowane. Bez /dev/null niechciane dane musiałyby być zapisane na dysk, a plik musiałby być później usunięty.

/dev/zero

Odczyt tego urządzenia zawsze daje bajty zerowe. Zapis zawsze kończy się powodzeniem a zapisane dane są po prostu kasowane.

/dev/full

Odczyt z tego urządzenia daje bajty zerowe. Zapis zawsze kończy się niepowodzeniem z komunikatem o braku miejsca. Przydatne do testowania jak program radzi sobie w sytuacji braku miejsca.

/dev/random, /dev/urandom

Te dwa urządzenia dają dostęp do losowych wartości zbieranych przez jądro z podłączonych do komputera urządzeń. Generowanie dobrej jakości liczb losowych inaczej byłoby dużym problemem.

W wypadku małej puli losowości w jądrze odczyt z /dev/random zablokuje się do czasu zebrania odpowiedniej ilości bitów entropii. Dane z /dev/urandom pochodzą z kryptograficznie bezpiecznego generatora liczb psuedolosowych i nie trzeba na nie czekać. W praktyce należy raczej używać urandom.

Pliki jako argumenty

Plik lub standardowe wejście

Wiele poleceń pracujących z tekstem, jeśli nie podamy argumentu będącego nazwą pliku (nie ma znaczenia czy plik istnieje czy nie), przyjmie, że ma czytać dane ze standardowego wejścia. W przypadku podania takich argumentów, odpowiednie działania będą podjęte na zawartości tych plików i standardowe wejście nie będzie brane pod uwagę (o ile tego osobno nie zażądamy).

/dev/stdin, /dev/stdout i -

Czasami chcemy móc się odwołać do standardowego wejścia czy wyjścia jakby było plikiem. Niektóre polecenia pozwalają podać jako nazwę pliku -, oznacza to, że trzeba w tym miejscu wziąć standardowe wejście. Niektóre polecenia nie stosują się jednak do tej konwencji. Zawsze można jednak użyć urządzeń /dev/stdin, /dev/stdout i /dev/stderr.

Potoki

Co to w istocie jest?

Potok (pipe) to narzędzie do komunikacji między dwoma procesami. Jeden z nich jest producentem, drugi konsumentem danych. Dane przepływają tylko w jedną stronę. Z potokiem stowarzyszony jest niewielki bufor na dane. Próba zapisu do potoku przy pełnym buforze spowoduje zatrzymanie zapisującego procesu do czasu opróżnienia przynajmniej części bufora. Podobnie próba odczytania z pustego bufora skończy się zablokowaniem procesu konsumenta.

Zalety i wady

Główną wadą jest fakt, że danych w potoku mieści się tylko skończona ilość (niewielka) i nie można przeskoczyć do przodu ani wrócić do tyłu. Główną zaletą potoku jest fakt, że nie musimy przechowywać plików pośrednich, które później trzeba by usunąć (albo na które mogłoby nie starczyć miejsca). Dzięki np. ssh można też łatwo przesyłać dane płynące potokiem przez sieć.

Potok nazwany

Potok nazwany to specjalny plik, który możemy otworzyć w dwóch procesach: w jednym do zapisu i w drugim do odczytu. Potoki nazwane tworzymy poleceniem mkfifo.

Potok anonimowy

Potok anonimowy tworzony jest przez funkcję systemową pipe w kodzie programu. Nie jest stowarzyszony z plikiem specjalnym, lecz końcami do odczytu i zapisu dzielą się używające potoku procesy.

Potoki w powłoce

Powłoka tworzy potoki anonimowe przy napotkaniu znaku |. Wyjście standardowe jednego polecenia łączone jest z wejściem standardowym następnego. Dzięki temu łatwo tworzyć bardziej skomplikowane polecenia z prostszych klocków.

Anegdota

W 1986 roku Donald Knuth został poproszony o napisanie programu do czasopisma Communications of the ACM. Program miał znajdować n najczęściej pojawiających się wyrazów w tekście. Knuth napisał niskopoziomowy program zawierający nowatorską strukturę danych i ciekawe podejście.

Anegdota cd.

Donald Knuth
Donald Knuth

Anegdota cd.

Program (w Pascalu) zajmował ok. 10 stron. Recenzent Doug McIlroy był pod wrażeniem programu Knutha, a recenzję zakończył następującym skryptem, robiącym to samo co program Knutha:

$ tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q

Dodajmy, że McIlroy jest pomysłodawcą idei potoku.

Wypisywanie argumentów na standardowe wyjście

echo

Polecenie wbudowane powłoki echo wypisuje podane argumenty (napisy) na standardowym wyjściu.

$ echo 1 2 3
$ echo -n 1 2 3
$ echo -e '\e[0;31mCZERWONO\e[0m'

Polecenia działające na całych plikach

cat

Polecenie cat (od concatenate) wypisuje zawartość podanych plików na standardowe wyjście. Gdy nie ma argumentów plikowych, kopiuje standardowe wejście na wyjście.

Opcje

  • -n – numeruj wypisywane linie

tac

Polecenie tac wypisuje każdy podany plik od ostatniej linii do pierwszej. Jeżeli nie podano pliku, robi to samo ze standardowym wejściem.

Opcje pozwalają na określenie co ma oddzielać poszczególne części pliku od siebie (zamiast znaku nowej linii).

od

Polecenie od wypisuje podane argumenty (lub standardowe wejście) w formacie ósemkowym.

Opcje

  • -x – używaj liczb szesnastkowych na wyjściu
  • -c – używaj znaków i symboli specjalnych jeśli to możliwe

nl

Polecenie nl numeruje linijki wyjścia. Opcje pozwalają na dokładniejsze określenie jak mają wyglądać numery linii itp.

base64

Polecenie base64 konwertuje bezstratnie podany plik binarny do pliku tekstowego zawierającego tylko znaki ASCII. Taka reprezentacja jest bardzo popularna w internecie.

Opcje

  • -d – dekodowanie z pliku tekstowego do binarnego
  • -w – zawijaj linie do podanej szerokości

Polecenia działające na częściach plików

tail

Polecenie tail wypisuje koniec podanych plików/standardowego wejścia. Domyślnie 10 ostatnich linii. Opcje pozwalają zna zmianę tej liczby oraz inne określenie końca pliku.

Polecenie to jest przydatne przy pozbywaniu się nagłówków z plików oraz przy pracy z najróżniejszymi logami (gdzie najczęściej interesuje nas to co zostało zapisane na końcu).

split

Polecenie split pozwala dzielić plik na w miarę równe kawałki (domyślnie po 1000 linii). Opcje pozwalają na zmianę tej wielkości czy określenie liczby docelowych plików, zamiast ich rozmiaru.

csplit

Polecenie csplit (context split) dzieli plik na kawałki, gdzie kawałki są wyznaczone przez podany wzorzec. W ten sposób możemy podzielić np. książkę na pliki wg rozdziałów itp. Wzorce to najczęściej napisy lub wyrażenia regularne.

$ csplit książka.txt '/Wstęp/' '/Rozdział/' '{7}' '/Epilog/'

Podsumowanie zawartości pliku

wc

Polecenie wc wyświetla liczbę linii, słów i bajtów w przekazanych plikach/standardowym wejściu.

Opcje

  • -c – wyświetla liczbę bajtów
  • -m – wyświetla liczbę znaków
  • -w – wyświetla liczbę słów
  • -l – wyświetla liczbę linii
  • -L – wyświetla długość najdłuższej linii

Sumy kontrolne

Polecenia md5sum, sha1sum, sha512sum obliczają i sprawdzają sumy kontrolne (MD5, SHA-1, SHA-2) dla plików.

Sortowanie

sort

Polecenie sort sortuje podane pliki leksykograficznie. Porządek sortowania wyznacza zmienna środowiskowa LC_COLLATE.

Opcje

  • -r – odwrotny porządek
  • -k – sortuj wg podanej kolumny
  • -t – traktuj podany znak jako separator kolumn
  • -n – sortuj jak liczby, nie leksykograficznie
  • -R – sortuj losowo: mieszaj, ale nie rozdzielaj tych samych wartości

shuf

Polecenie shuf miesza (tasuje) linijki podanych plików. Każda permutacja na wyjściu jest równo prawdopodobna.

uniq

Polecenie uniq usuwa powtarzające się, sąsiadujące linie. Często przed jego użyciem tekst trzeba posortować.

Opcje

  • -c – obok każdej linijki napisz ile razy występowała w oryginale
  • -d – wypisz tylko powtarzające się linijki
  • -u – wypisz tylko unikalne linijki

Polecenia działające na polach/kolumnach

cut

Polecenie cut pozwala wyciąć z każdego wiersza interesujące nas informacje. Mogą być to odpowiednie bajty, znaki czy kolumny.

$ cut -d ' ' -f -5,8,10

paste

Polecenie paste przyjmuje jako argument parę plików i wypisuje linijkę z jednego pliku, tabulator i linijkę z drugiego pliku. Brakujące linijki traktowane są jak puste.

join

Poleceni ejoin pozwala na wykonanie złączenia dwóch plików tekstowych na podstawie wspólnego pola, jak w SQL-u. Oba pliki muszą być posortowane względem kolumny wg której następuje złączenie.

Polecenie działające na znakach

tr

Polecenie tr zamienia znaki standardowego wejścia z jednego zbioru na odpowiadające im znaki z drugiego zbioru (np. duże na małe litery).

$ tr A-Z a-z
$ tr -s ' '
$ tr -d 0-9

Wyszukiwanie

Wyrażenie regularne

Wyrażenia regularne to bardzo elastyczny mechanizm wyszukiwania wykorzystujący metaznaki, trochę podobny do poznanego już wyszukiwania plików przez powłokę (globbingu). Istnieje wiele silników wyrażeń regularnych, o podobnej składni lecz różniących się szczegółami i siłą wyrazu. Jednymi ze słabszych, ale najbardziej popularnych są POSIX-owe wyrażenia regularne.

Wyrażenia regularne POSIX-Extended (ERE)

Przykładowe metaznaki:

  • . – dowolny znak
  • [abc] – dowolny znak spośród wymienionych
  • ^ – początek linii
  • $ – koniec linii
  • * – powtórz poprzedni element dowolnie wiele razy (także zero)
  • ( ) – grupa
  • \1, \2 – pierwsza grupa, druga grupa, itp.
  • [:upper:] – duża litera

Przykładowe wyrażenia

  • ^www\..*\.pl$
  • .*@.*\..*
  • [:digit:]{11}
  • (.)(.)(.)\2\1

grep

Narzędzie grep wypisuje tylko te linie podanych plików/standardowego wejścia, które pasują do zadanego wyrażenia regularnego. Dostępne są obie składnie wyrażeń (BRE i ERE).

Opcje

  • -E – używaj składni ERE zamiast domyślnej BRE
  • -F – szukaj zwykłego napisu (nie wyrażenia regularnego)
  • -R – wyszukuj rekurencyjnie w plikach
  • -v – wyświetlaj tylko linie nie pasujące do wyrażenia
  • -i – nie rozróżniaj dużych/małych znaków
  • -c – dla pliku wyświetl tylko liczbę pasujących linii
  • --color=always – wyświetlaj linie z zaznaczonym pasującym ciągiem znaków
  • -C 1 – wyświetlaj linie obok linii pasujących (kontekst)

sed

Narzędzie sed (stream editor) to złożone i dosyć przydatne narzędzie do edycji tekstu przy użyciu wyrażeń regularnych. W istocie w postaci ciągu poleceń sed-a można zrealizować każdą maszynę Turinga.

$ sed -ri 's/([^ ]*) Nowak/\1 Kowalska/' *

Inne przydatne polecenia

tee

Polecenie tee kopiuje standardowe wejście na standardowe wyjście i dodatkowo zapisuje kopię do podanego pliku.

pv

Polecenie pv kopiuje standardowe wejście na standardowe wyjście, a na standardowym wyjściu błędów wypisuje aktualną prędkość transfer.

less

Polecenie less to tzw. pager: wyświetla długie pliki na jednym ekranie. Umożliwia nawigację i wyszukiwania. Strony manuala są domyślnie wyświetlane przez less.

Archiwizacja i kompresja

Archiwizacja

Archiwizacja danych to zebranie ich danych i metadanych w postaci jednego pliku (nieskompresowanego), który łatwiej przenosić i kopiować. Do archiwizacji służy program tar.

Kompresja

Standardowe kompresory działające na Linuksie są kompresorami strumieniowymi – są zaprojektowane tak, aby kompresować dane płynące np. przez potok. Dlatego potrafią kompresować naraz tylko jeden plik - tym samym dobrze uzupełniają się z tar-em. Pod linuksem często można spotkać pliki z rozszerzeniem tar.gz, tar.bz2, tar.xz. Są to archiwa tar spakowane programem, odpowiednio: gzip, bzip2 i xz. Odpowiednie narzędzia do rozpakowywania to gunzip, bunzip2 i unxz. Polecenia zcat, bzcat i xzcat dekompresują plik i wysyłają zawartość na standardowe wyjście.

Oba kroki naraz

Znane państwu formaty zip, rar i 7z łączą archiwizację i kompresję. Najczęściej nie rodzi to problemów, ale czasem może wiązać się z utratą niektórych metadanych (uprawnienia itp.).