Procesy i wątki

Proces

Proces to swojego rodzaju logiczny kontener, który zawiera m.in.:

  • Listę wątków
  • Mapę pamięci procesu
  • Uchwyty do otwartych plików, połączeń sieciowych, urządzeń itp.
  • Dodatkowe informacje potrzebne systemowi do zarządzania danym procesem
  • Obsługę sygnałów, procesów potomnych

Wydzielenie takie – w postaci procesu, daje systemowi operacyjnemu możliwość zarządzania uruchomionymi programami. Scheduler systemowy (planista) przydziela czas procesora poszczególnym procesom uruchomionym w systemie. Konieczność przełączania pomiędzy procesami spowodowana jest tym, że danej chwili rdzeń procesora może wykonywać jeden wątek. Z punktu widzenia procesora na komputerze wykonywany jest jeden program, a z punktu widzenia systemu jednocześnie wykonywanych jest wiele programów. 

Współczesne procesory mogą wykonywać kilka wątków równocześnie, w zależności od liczby rdzeni. W tym momencie warto zauważyć, że proces po uruchomieniu posiada przynajmniej jeden wątek, z poziomu którego programowo można utworzyć kolejne. Wątek jest odpowidzialny za wykonywanie kodu programu.

Uruchomienie aplikacji powoduje utworzenie w systemie nowego procesu. Wyjątkiem może być np. Skrypt Java Script uruchomiony w przeglądarce, gdzie interpreter jest współdzielony przez wiele skryptów. Każdy proces ma wydzieloną przestrzeń adresową, która jest zapełniania i zwalniana podczas działania programu. Dostęp do tej pamięci przez inne procesy jest ograniczony. Proces zawiera także informacje zarządzane przez system operacyjny np.: identyfikator PID; czas życia uptime; ścieżka do katalogu roboczego; lista otwartych uchwytów; priorytet; lista argumentów.

Tworzenie procesu

Poniżej przedstawiam prosty przykład tworzenia nowego procesu z wykorzystaniem niskopoziomowego API systemów unixowych. Użyta została funkcja fork, która wykonuje kopię obecnego procesu.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>


int main(void)
{
    pid_t pid = fork();
    if(pid == 0)
    {
        //Kod procesu dziecka
        printf("Child Process\n");
    }
    else if(pid > 0)
    {
        //Kod procesu rodzica
        printf("Parent Process\n");
    }
    else
    {
        //Błąd
        perror("Failed to create process\n");
    }

    return 0;
}

Kompilacja i uruchomienie:

gcc -Wall -Wextra fork.c
./a.out
Parent Process
Child Process

Wątek

Wątek w praktyce jest jednostką wykonującą kod programu. Programiści tworzą wiele wątków działających w obrębie jednego procesu w celu uproszczenia struktury/architektury programu, wydzielenia logicznych części programu oraz zwiększenie wydajności przez zrównokeglenie obliczeń. 


Różnica pomiędzy wątkiem a procesem polega na tym, że wątki w obrębie tego samego procesu współdzielą między sobą pamięć adresową, uchwyty do urządzeń, plików, połączeń (proces ma przydzielone niezależne zasoby).Dzięki temu wymagają mniej zasobów do działania, krótszy jest ich czas tworzenia/zakończenia, komunikacja/współdzielenie zasobów może odbywać się bez pomocy jądra systemu oraz możliwość szybkiego przełączania kontekstu pomiędzy wątkami tego samego procesu. Możliwość jednoczesnego wykonywania kodu korzystającego z tych samych zasobów wymaga zapewnienia zsynchronizowanego dostępu do danych w celu uniknięcia błędów w obliczeniach, zakleszczenia wątków czy błędów bezpieczeństwa.

Tworzenie wątków

Poniżej przykład utworzenia nowych wątków z wykorzystaniem API zgodnego z POSIX.

#include <stdio.h>
#include <pthread.h>

void *thread_print(void *data);

int main(void)
{
    pthread_t th1, th2;
    int ret1, ret2;

    ret1 = pthread_create(&th1, NULL, thread_print, (void *)"Message 1");
    if(ret1 != 0)
    {
        fprintf(stderr, "Error: creating thread\n");
        return 1;
    }

    ret2 = pthread_create(&th2, NULL, thread_print, (void *)"Message 2");
    if(ret2 != 0)
    {
        fprintf(stderr, "Error: creating thread\n");
        return 1;
    }

    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    return 0;
}

void *thread_print(void *data)
{
    char *msg = (char *)data;
    printf("%s\n", msg);
    return NULL;
}

Kompilacja i uruchomienie: 

gcc -Wall -Wextra thread.c -lpthread
./a.out
Message 1
Message 2

UDP vs TCP

UDP

UDP(User Datagram Protocol) jest szybkim i prostym protokołem. Działa w oparciu o protokół IP. Wprowadza kilka dodatkowych paramentów na protokół IP, dzięki którym system może  przekazać dany pakiet do konkretnej aplikacji oraz sprawdzić czy nie uległ uszkodzeniu. Narzut ze strony nagłówka jest minimalny, co z kolej przekłada się na mały narzut wydajności.

Rysunek poniżej przedstawia strukturę nagłówka UDP.

Port UDP działa w oparciu o mechanizm portów (który również występuje w TCP). Port to 16-bitowa liczba, wybrana przez aplikację wysyłającą lub odbierającą dane. W niektórych systemach operacyjnych wybranie portów z przedziału 0-1023 wymaga uprawnień administratora. Mechanizm portów pozwala kierować dane (lub informację o połączeniu) do konkretnej aplikacji lub usługi. Umożliwia to jednoczesną wymianę danych pomiędzy wieloma programami. Nadawca wiadomości musi podać numer portu docelowego, aby system mógł przekazać pakiet do właściwej aplikacji. 

TCP

Do zapewnienia niezawodnej komunikacji wykorzystuje się protokół TCP(Transmission Control Protocol). Podobnie jak UDP działa w warstwie transportowej (czwarta warstwa). TCP stara się zagwarantować jak najwyższą niezawodność połączenia oraz integralność przesyłanych strumieniowo danych. W przeciwieństwie do UDP podczas transmisji TCP można mieć pewność, że dane dotrą w takim stanie jakim zostały wysłane. Jeśli korekcja błędów nie będzie możliwa, to rozmowa zostanie porzucona. Często obarczone jest ponownym wysyłaniem pakietów.

Struktura nagłówka TCP przedstawiona jest na rysunku poniżej.

Warto zwrócić uwagę na:

Bity sterujące m.in. ACK i SYN – wykorzystywane podczas nawiązywania połączenia.Suma kontrolna – 16-bitowa, umożliwia wykrycie uszkodzonych danych.Numer sekwencyjny oraz numer potwierdzający – służą do zapewnienia właściwej kolejności odbioru danych zgodnej z kolejnością ich wysyłania oraz do kontroli, czy wszystkie pakiety dotarły do odbiorcy.

TCP jest protokołem połączeniowym. W celu nawiązania połączenia jedna ze stron tworzy gniazdo nasłuchujące i wiąże je z określonym portem TCP oraz lokalnym adresem IP interfejsu sieciowego (lub może być powiązane z portem na wszystkich dostępnych interfejsach sieciowych – wówczas za adres IP przypisuje się 0.0.0.0). 

Gdy gniazdo nasłuchujące jest gotowe, druga strona komunikacji może zainicjajizować połączenie. Odbywa się to to za pomocą mechanizmu trójetapowego negocjowania połączenia (three-way handshake). Przesyłane są specjalne pakiety (bez danych) do ustalenia celu komunikacji, potwierdzenia tożsamości i ustalenia początkowych numerów sekwencyjnych i potwierdzających. W uproszczeniu proces ten wygląda następująco:

  • Klient wysyła do serwera pusty pakiet z ustawionym bitem SYN oraz początkowym numerem sekwencyjnym
  • Serwer wysyła do klienta pakiet z ustawionymi bitami SYN i ACK
  • Klient wysyła do serwera pakiet z ustawionym bitem ACK

Negocjowanie połączenia eliminuje możliwość prostego podszycia się pod jednego z rozmówców (istotne jest wybranie trudnych do przewidzenia początkowych numerów sekwencyjnych). Po udanej negocjacji połączenie jest nawiązane. Po stronie serwera zostaje utworzone drugie gniazdo, reprezentujące nawiązane połączenie, które służyć będzie do wymiany danych pomiędzy stronami. Gniazdo nasłuchujące nadal pozostaje aktywne i może służyć do nawiązania kolejnych połączeń.Zakończenie sesji następuje, gdy jedna ze stron wyśle pakiet z ustawionym bitem FIN oraz odpowiednim numerem sekwencyjnym. Jest możliwe również zakończenie połączenia w dowolnym momencie poprzez wysłanie pakietu RST.

Podsumowanie

TCPUDP
AkronimTransmission Control ProtocolUser Datagram Protocol
StanowośćPołączeniowyBezpołączeniowy
Kontrola danych16-bitowa suma kontrolna16-bitowa suma kontrolna
Reakcja na uszkodzone danePonowienie transmisji
Odrzucenie pakietu
Kolejność danychKolejność odebranych danych zgodna z kolejnością ich wysyłaniaBrak
PotwierdzeniaPotwierdzenia po otrzymaniu segmentu danychBrak
Rozpoczęcie transmisjiObie stony negocjują/potwierdzają chęć połączeniaBrak
Zakończenie transmisjiJedna ze stron może zażądać zakończenia połączenia lub po określonym czasie braku aktywności którejkolwiek ze stronBrak
Prędkość transmisjiWolniejszy niż UDPSzybszy niż TCP
Używany przez
HTTP, HTTPS, FTP, SMTP, Telnet
DNS, DHCP, TFTP, VOIP 

Mode OSI

Opisów modelu OSI w sieci nie brakuje. Niniejszy wpis ma bardziej formę notatki zawierającej najważniejsze informację niż dokładnej analizy tego zagadnienia. Zapraszam do lektury. 


Model OSI (Open Systems Interconnection) jest modelem warstwowym w którym system sieciowy podzielony jest na 7 warstw. Podział na warstwy ułatwia określenie reguł i zasad komunikacji – protokołów komunikacyjnych. Umożliwia to współdziałanie urządzeń i oprogramowania różnych producentów oraz przede wszystkim ułatwia zarządzanie procesem komunikacji. Model dzieli komunikację na mniejsze łatwiejsze do zarządzania etapy oraz określa zadania jakie muszą być realizowane w poszczególnych warstwach. Model OSI umiejscawia określony protokół w odpowiedniej warstwie abstrakcji.


Rysunek poniżej przedstawia warstwy modelu OSI ISO oraz najważniejsze protokoły pracujące w danej warstwie.

Rola poszczególnych warstw:

Warstwa fizyczna: 

  • definiuje parametry elektryczne nośnika danych (poziomy napięć, długość fali, modulacja) 
  • transmisja surowych danych za pomocą medium fizycznego
  • jednostka danych – bit

Warstwa łącza danych:

  • obsługa medium transmisyjnego
  • kodowanie (niezalecane jest wysyłanie długich sekwencji samych zer lub jedynek)
  • synchronizacja odbiornika do nadajnika
  • jednostka danych – ramka

Warstwa sieciowa:

  • adresowania
  • trasowanie czyli routing – znajdowanie drogi do odbiorcy
  • jednostka danych – pakiet

Warstwa transportowa:

  • zapewnienie jak najwyższej niezawodności i odporności na błędy
  • sposób w jaki sposób dane przesyłane są przez sieć, podstawowe protokoły to TCP, UDP
  • nie musi znać topologi sieci, gdzie znajduje się odbiorca
  • jednostka danych – segment (TCP), datagram (UDP)

Warstwa sesji:

  • utrzymanie połączenia pomiędzy wysokopoziomowymi stronami
  • multipleksowanie danych różnych aplikacji
  • może odbywać się szyfrowanie 
  • jednostka danych – dane

Warstwa prezentacji:

  • ustalenie jednolitego formatu danych
  • porządek bajtów little/big-endian
  • kodowanie liczb np. uzupełnienie do 2
  • kodowanie napisów np. ASCII, UTF-8
  • szyfrowanie(SSH, TLS/SSL), kompresja przesyłanych danych
  • jednostka danych – dane

Warstwa aplikacji:

  • pozwala korzystać z sieci jej użytkownikom
  • jednostka danych – dane

Dane wysłane przez nadawce przechodzą przez poszczególne warstwy od najwyższej do najniższej (w dół stosu). Są one dzielone na odpowiednie fragmenty oraz dodawane są informacje sterujące. Proces ten nazywamy enkapsulacją.  Odbiorca w celu otrzymania poprawnych danych przeprowadza proces odwrotny – dekapsulację. Przepływ danych występuje w górę stosu.

Ethernet

Warstwa fizyczna – PHY, odpowiada za współpracę z właściwym medium transmisyjnym. Natomiast MAC (Media Access Control) za obsługę protokołu Ethernet.MAC z PHY połączone są za pomocą MII, RMII, GMII


Rysunek poniżej przedstawia budowę rakmi ethernet

Aby istniała możliwość detekcji zajętości łącza, to pierwszy bit wysyłanj ramki musi dotrzeć do najdalszego zakątka sieci zanim zostanie wysłany ostatni bit ramki. Stąd minimalna długość pola danych.

Protokół IPv4

Działa w warstwie trzeciej i wprowadza:

  • adresacje IP – 32-bitowe adresy, które zazwyczaj zapisane są w postaci 1-bajtowych wartości oddzielonych kropkami
  • routing, czyli przekazywanie pakietów pomiędzy sieciami

Adres IP składa się z prefiksu i sufiksu, te dwie części wyróżnione są za pomocą maski.Prefiks wraz z maską wyznaczają pulę adresów do której należą wszystkie adresy mające ten sam prefiks.192.168.51.0/24 oznacza przedział 192.168.51.0 do 192.168.51.255

Pule adresów prywatnych:

10.0.0.0 do 10.255.255.255
172.16.0.0 do 172.31.255.255
192.168.0.0 do 192.168.255.255

Podsieci łączy się za pomocą routerów (ma wile interfejsów sieciowych).Rysunek poniżej przedstawia budowę datagramu IP

Jako pierwszy przesyłany jest najbardziej znaczący oktet – big-endian. Jest to tzw. porządek sieciowy oktetów.

ARP – tłumaczenie adresów sieciowych na adresy sprzętowe.

ARP – Address Resolution Protocol

Ethernet używa adresów MAC (sprzętowych). Komunikat ARP podróżuje w danych ramki ethernetowej, która w polu typ ma wartość – 0x0806. Ramka zawiera m.in. pola: adres sieciowy odbiorcy, adres sieciowy nadawcy, adres sprzętowy odbiorcy, adres sprzętowy nadawcy, pole operacja. Węzeł chcący wysłać datagram IP do odbiorcy którego adresy ethernetowego nie zna, rozgłasza do wszystkich węzłów (na adres ff:ff:ff:ff:ff:ff) w swojej podsieci zapytanie ARP (wartość – 1 w polu operacja). Nadawca pole – adres sprzętowy nadawcy oraz adres sieciowy nadawcy uzupełnia swoimi adresami. Natomiast w polu – adres sieciowy odbiorcy umieszcza IP węzła, którego adres ethernetowy chce otrzymać.
Węzeł który ma poszukiwany adres IP odpowiada bezpośrednio pytającemu, gdyż zna jego adres sprzętowy (otrzymał go w zapytaniu). W odpowiedzi pole operacja ma wartość – 2. W odpowiedzi nadawca pola –  adres sieciowy nadawcy i adres ethernetowy nadawcy uzupełnia swoimi adresami.Aby poprawić efektywność komunikacji węzły utrzymują w pamięci podręcznej (cache) tablice ARP, która zawiera poznane dotychczas przyporządkowania adresu ethernetowego do IP. Adresy są aktualizowane przez wysłanie zapytania oraz gdy zapytania otrzymują nawet jeśli nie były one do danego węzła bezpośrednio kierowane.