Korzenie systemu operacyjnego Unix sięgają aż do roku 1969, kiedy to Ken Thompson, jedyny programista tego systemu opracował pierwszą wersję Uniksa. Przez kolejne lata był gruntownie rozwijany, aby w końcu stać się tym, czym jest dzisiaj. W tym czasie powstało wiele wersji systemu na różnego rodzaju komputery, między innymi na platformę x86.
Unix został zaprojektowany jako system wielozadaniowy, wieloprocesowy z podziałem czasu między procesami. Interfejsem użytkownika jest tekstowa konsola, nazywana także powłoką (z ang. shell). Większość systemu została napisana w języku C, co dało możliwość przenoszenia systemu na różne platformy. W tym artykule opiszę wersję systemu 4.3BSD ze względu na wprowadzone tam ciekawe rozwiązania, a z drugiej strony na możliwość łatwego zrozumienia funkcjonowania tej właśnie wersji oraz dysponowania przeze mnie dokumentacji akurat tej wersji.
Jądro Uniksa jest typowym kernelem monolitycznym (patrz Budowa systemu -> Jądro systemu), a więc odpowiada za obsługę większości zadań systemu operacyjnego. Posiada w sobie obsługę takich rzeczy jak sygnały, terminale, system wejścia-wyjścia, systemy plików, zarządzanie pamięcią, wymiana pamięci, planowanie przydziału procesora oraz obsługa różnego rodzaju sprzętu. Warstwie aplikacji jądro systemowe udostępnia szereg funkcji systemowych (z ang. system calls) pogrupowanych w cztery kategorie - manipulowanie plikami, sterowanie procesami, manipulowanie informacją oraz manipulowanie urządzeniami (które są także plikami, więc często tą kategorię się pomija).
W systemie Unix plikiem jest ciąg bajtów o dowolnej strukturze, której jądro nie narzuca. Plikiem są także foldery (struktura organizacyjna) oraz urządzenia (pliki specjalne). Pliki są zorganizowane w drzewiastą strukturę katalogów (folderów), której szczytem jest folder zwany root, a oznaczany znakiem /. Na szczycie struktury katalogów znajdują się katalogi pełniące różne funkcje, na przykład w folderze /bin/ znajdują się aplikacje wykonywalne, a w folderze /home/ umieszczone są zwykle katalogi domowe użytkowników systemu, na przykład /home/Tomek. Trzeba także dodać, że w systemie Unix duże i małe litery w nazwach plików (folderów, urządzeń) są rozróżnialne, tak więc plik /home/Tomek/plik nie jest tym samym plikiem co /home/Tomek/PLIK. Jak już wspomniałem, urządzenia są specjalnym rodzajem plików i zwykle umieszczone są w katalogu /dev/ (skrót od devices - z ang. urządzenia), przykładowym urządzeniem jest /dev/console, czyli po prostu konsola. Ciekawym rozwiązaniem jest zastosowanie w systemie plików tak zwanych dowiązań symbolicznych. Dowiązanie symboliczne umożliwia utworzenie wirtualnej kopii danego pliku znajdującej się w innym folderze. Modyfikacja takiego pliku wiąże się z modyfikacją pliku oryginalnego. Jądro systemowe udostępnia funkcje systemowe do wykonywania podstawowych operacji na plikach takich jak utworzenie pliku lub folderu, usunięcie pliku lub folderu, otwarcie lub zamknięcie pliku, odczyt oraz zapis pliku, i kilka innych (zachęcam do zapoznania się z dokumentacją tego systemu).
Procesem w systemie Unix jest każdy wykonujący się program. Proces może utworzyć nowy proces przez wywołanie funkcji systemowej fork, tworzącej nowy proces (proces potomny) będący wierną kopią procesu wywołującego, zwanego także procesem macierzystym. Nowy proces kontynuuje swoją pracę, tak samo jak proces macierzysty, z tą różnicą, że funkcja systemowa fork zwraca obydwu procesom inne wartości, w celu rozróżnienia procesu macierzystego od potomnego (proces potomny otrzymuje wartość zero, natomiast macierzysty otrzymuje identyfikator swojego procesu potomnego). Proces potomny zwykle po utworzeniu wywołuje funkcję systemową execve, która zamazuje proces nowo załadowaną aplikacją i oddaje kontrolę tej aplikacji. Proces macierzysty może czekać na zakończenie procesu potomnego, przez wywołanie funkcji systemowej wait lub wait3, stając się przy tym tak zwanym procesem zombie (będącym w stanie oczekiwania, nie wykonującym żadnego zadania). W celu umożlwienia komunikacji między procesami, wprowadzono system specjalnych łącz komunikacyjnych (z ang. pipes - rury). Łącze to jest po prostu kolejką bajtów z której jeden proces czyta, a drugi do niej zapisuje. W razie próby odczytu z pustego łącza, proces przechodzi w stan oczekiwania na dane, podobnie jest z próbą zapisu do pełnej kolejki.
Aby umożliwić sterowanie pracą aplikacji, wprowadzono specjalny system sygnałów wysyłanych do aplikacji, która może odpowiednio na nie zareagować. Źródłem sygnału może być skrót klawiaturowy (np. CTRL + C powodujący zakończenie aplikacji), błąd aplikacji, przerwanie sprzętowe lub wywołanie funkcji systemowej kill z odpowiednim parametrem. Sygnały służą zwykle do odpowiedniego (mniej lub bardziej brutalnego) zakończenia pracy aplikacji z powiadomieniem jej o tym, aby zabezpieczyć się przed utratą danych.
Interfejsem systemu operacyjnego Unix jest typowa konsola tekstowa, zwana shellem lub powłoką. Powłoka działa na zasadzie dialogu z użytkownikiem, gdy jest gotowa na przyjęcie polecenia wyświetla tzw. znak zachęty (np. znak >). Wprowadznie polecenia opiera się na wpisaniu jego nazwy z klawiatury i wciśnięcie przycisku ENTER (czasami zwany RETURN). Podczas wykonywania, aplikacja może wyświetlać rózne komunikaty na konsoli informując użytkownika o jej przebiegu. Może także pobrać pewne dane od użytkownika, który wpisuje je za pomocą klawiatury. Gdy aplikacja zakończy działanie, shell jest gotowy do przetworzenia następnego polecenia. Oprócz możliwości wykonywania zewnęrznych aplikacji, shell posiada zwykle także polecenia wbudowane, na przykład listowanie katalogów lub kopiowanie, usuwanie i przenoszenie plików. Istnieje kilka róznych wariantów powłok uniksowych. Najbardziej znanymi są SH (SHell Bourne'a), BASH (Bourne-Again SHell - zmodyfikowany i ulepszony shell Bourne'a), CSH (C SHell - powłoka o składni języka C) oraz KSH (Korn SHell - shell Korn'a), każdy posiada swoje wady i zalety, a także zastosowania, w których sprawdza się najlepiej.
Planowanie przydziału procesora w systemie Unix opiera się na algorytmie rotacyjnym z uwzględnieniem priorytetów. Tak więc aplikacja z wyższym priorytetem (numerycznie niższym) otrzyma więcej czasu procesora niż ta z niższym. Procesy wykonujące ważne operacje systemowe i dyskowe mają bardzo wysoki priorytet i nie można ich zatrzymać za pomocą sygnałów. Proces może zrzec się swojego czasu procesora przez funkcję systemową sleep, oczekując na pewne wydarzenie, a gdy ono wystąpi system operacyjny za pomocą funkcji wakeup budzi dany proces.
Unix używa do adresowania pamięci systemu stronnicowania z wymianą pamięci, w razie dużego zapotrzebowania na pamięć. Wymianie ulegają strony pamięci w ilości będącej potęgą cyfry 2, np. 32 strony. Największy obszar do wymiany jest ograniczony rozmiarem pamięci dyskowej przeznaczonej na plik/partycję wymiany. Od wersji 4.2BSD wprowadzono system stronnicowania na żądanie, który działa na prostej zasadzie wczytywania poszczególnych strony z dysku przy pierwszej próbie odczytu z niej lub zapisu do niej. Strony przeznaczone do wymiany wybierane są na podstawie algorytmu LRU (najdawniej używanych).