HW 9 - Vícevláknová aplikace s meziprocesovou komunikací

Termín odevzdání 21.05.2024@23:59 PDT
Povinné zadání 6b
Volitelné zadání Není
Bonusové zadání HW 9B
Počet uploadů 20
Výchozí soubory bab36prga-hw9.zip

Cílem úlohy je získat zkušenost s využitím pojmenované roury pro komunikaci aplikace v počítači s poskytnutým Modulem a práci s více vlákny pro zpracování více zdrojů událostí.

Program vychází ze cvičení 10 pojmenovanou rourou a více-vláknové aplikace.
Specifikum práce s pojmenovanou rourou je, že při otevření souboru (roury) pro zápis musí být roura otevřena pro čtení, aby bylo kam případně ukládat data. Proto je volání io_open_write() blokované. Řešením je tak spustit program výpočetního modulu ./binaries/bab36prga-hw9-module nebo vyčítání roury programem tail -f /tmp/prga-hw9.in.

Povinné zadání

Realizujte program, kterým bude řízeno blikání LED v Modulu.
Program musí obsahovat následující funcionality:

  • Čte klávesy (bez nutnosti stisku klávesy enter) a reaguje na stisk kláves se znaky 's', 'e', 'h', 'b', a '1'-'5', které přeposílá Modulu.
  • Při stisku klávesy 'q' ukončuje program svou činnost (koretně zastaví všechna vlákna).
  • Průběžně zobrazuje stav LED, poslední odeslaný a přijatý příkaz, peridou blikání a počet přijatých informací o počtu rozsvěcení LED ve tvaru:
    "\rLED %3s send: '%c' received: '%c', T = %4d ms, ticker = %4d"
    kde jednotlivé řidící znaky formátování znamenají:
    • %3s je on nebo off podle stavu LED,
    • %c je poslední vyslaný znak (mezera ' ' po startu),
    • další %c je posledně přijatý znak ('?' v případě nové zprávy zatím bez odpovědi),
    • %4d je doba rozsvěcení/zhasnnutí LED (uvažujeme stejnou dobu, kdy LED svítí a kdy je zhasnutá, tj. PWM se střídou 1:1) odhadnutá na základě přijatých zpráv od Modulu za časový interval 5 sekund (výpis hodnoty periody je zarovnaný na 4 pozice),
    • poslední %4d je počet přijatý znaků 'x' indikující změnu stavu LED při blikání.

Pro výpis využijte přepisování řádku znakem '\r'. Po odeslaní znaku 's' a 'e' je stav LED (on nebo off) nastaven až po přijeti potvrzovacího znaku 'a'.

Důležitou částí je organizace kódu a dedikování jednotlivých vláken pro 1) čtení z klávesnice; 2) překreslování řádku, 3) čtení z roury; 4) měření periody blikání LED jednou za 5 sekund; případně další hlavní vlákno v boss/worker modelu.
Implementačně (na počet řádků) náročnější variantou je realizovat v program frontu zpráv a řešit logiku programu v rámci hlavního vlákna, které přijímá zprávy od ostatních vláken a realizuje příslušné výstupy (tj. pojmenovaná roura a textový výstup na stdout. Benefitem takto organizovaného programu je snadné rozšíření.
Jednodušší variantou je dedikovat zápis do roury vláknu pro čtení vstupu z klávesnice a notifikace překreslování z jednolivých vláken na základě přijaté události (tak jako v programu na přednášce 8 - b3b36prg-lec08-codes.zip).
Komunikace mezi aplikací a Modulem
  • 'i' - init - může vyslat Modul po startu, žádná reakce není očekávána.
  • 's' - start - vysílá PC, Modul reaguje rozsvícením LED a odpovědí 'a'.
  • '1' - set 1 - vysílá PC, Modul reaguje nastavením periody blikání LED na 50 ms (tj. 50 ms LED svítí a 50 ms je zhasnuta) a odpovědí 'a'.
  • '2' - set 2 - vysílá PC, Modul reaguje nastavením periody blikání LED na 100 ms a odpovědí 'a'.
  • '3' - set 3 - vysílá PC, Modul reaguje nastavením periody blikání LED na 200 ms a odpovědí 'a'.
  • '4' - set 4 - vysílá PC, Modul reaguje nastavením periody blikání LED na 500 ms a odpovědí 'a'.
  • '5' - set 5 - vysílá PC, Modul reaguje nastavením periody blikání LED na 1000 ms a odpovědí 'a'.
  • 'e' - end - vysílá PC, Modul reaguje zhasnutím LED a odpovědí 'a'.
  • 'h' - hello - vysílá PC, Modul reaguje zasláním 'h' a výpisem “hello” na stdout.
  • 'b' - bye - vysílá PC, Modul odpovídá 'b' a reaguje opuštěním main() funkce a ovládací program následně po přijetí 'b' ukončuje svou činnost. Aby bylo možné ukončit program po přijetí znaku 'b', je nutné ukončit načítání znaku ze stdin, např. neblokovaným přístup s pollingem podobně jako čtení z roury.

Dále Modul v případě, že periodicky bliká LED, vysílá znak 'x' při rozsvícení LED a znak 'o' při zhasnutí LED.

Doporučený postup (bez hlavní fronty zpráv)

Program obsluhuje dva zdroje asynchronních událostí - z klavesnice a z modulu. Pro každý takový zdroj je výhodné realizovat samostatné vlákno, které řeší konkrétní vstup. Kromě toho můžeme identifikovat proces počítání periody na základě přijatých zpráv z Modulu, ze kterých počítáme průměrnou periodu za 5 sekund, což můžeme realizovat samostatným vláknem, které periodicky po 5 sekundách spočítá průměr. V neposlední řadě máme společný sdílený prostředek, kterým je výstup na terminál v podobě stavového řádku, to můžeme realizovat jako sdílený přístup ke zdroji prostřednictvím zámku (mutexu), nicméně pak by každé vlákno musele vypisovat celý řádek. Proto může být výhodnější realizovat samostnatné vlákno, které bude vypisovat stav aplikace pokud dojde k nějaké změně (realizujeme jako čekání na podmíněnou proměnnou conditional variable. Dalším sdíleným prostředkem je zasílání zpráv modulu, nicméně z principu funkce programu se to děje pouze po uživatelské interakce, tj. stisku klávesy. Program tak realizujeme Boss/worker architekturou, kde hlavní vláknou vytvoří jednotlivá vlákna a následně bude čekat na jejich ukončení, podobně jako v příkladu z přednášky 10.

Sdílená datová struktura

Mezi vlákny můžeme sdílet společnou strukturu, ve které budeme mít informace potřebné k výpisu na stdout, tj. stav LED, poslední odeslaný znak, poslední přijatý znak, odhadnutá perioda blikání. Dále pak informaci o požadavkuna ukončení programu, tak abychom korektně ukončili všechna vlákna, informaci o počtu přijatých bliknutích LED, případně odkazy na soubory pro čtení a zápis (file descriptor) pojmenované roury pro čtení a zápis realizující komunikaci s Modulem, to pokud otevření souborů realizujeme v hlavním vlákně, což může být výhodné, pokud se otevření nezdaří, nebudeme ani ostatní vlákna pouštět. Dále též proměnnou typu mutex a podmíněnnou proměnnou pro synchronizaci vláken.

Hlavní vlákno

  • Incializuje sdílenou datovou strukturu a struktury pro hromadnou správu vláken.
  • Otevírá soubory (roury) pro komunikaci.
  • Vytváří vlákna
  • Čeká na ukončení vláken.

Vlákno obsluhy klávesnice

  • Smyčka čtení z klávesnice.
  • Vláknou, které inciuje ukončení programu, po stisku 'q'.
  • Reaguje na stisky jednotlivých kláves.
  • Dle stisknuté klávesy, zasílá zprávu modulu a mění stav programu.
  • Pokud dojde ke změně stavu programu, může informovat zobrazovací vlákno.
  • Po ukončení smyčky načítání z klávesnice, informuje všechna případně blokovaná vlákna (pthread_cond_broadcast())

Vlákno obsluhy zpráv z Modulu

  • Periodicky čte ze souboru (pojmenované roury).
  • Využívá funkce io_getc_timeout() pro neblokované čtení, aby bylo případně vlákno možné ukončit.
  • Pokud nastal požadavek na ukončení programu, ukončuje cyklus načítání ze souboru.
  • Přijatou zprávu (znak) nastavuje do sdílené datové struktury (stavu programu).
  • Na příjem zprávy 'o' inkrementuje počítadlo rozsvícení LED a nastavuje stav LED na zapnuto.
  • Na příjem zprávy 'x' nastavuje stav LED na vypnuto.
  • Na příjem potvrzení předchozí zprávy ('s' nebo 'e') reaguje změnou nastavení stavu LED.
  • V případně změnu stavu notifikuje výstupní vláknou, např. pthread_cond_broadcast().
  • V případě chyby načítání se vlákno ukončuje, nastavuje požadavek na ukončení (např. shared_data→quit = true; a notifikuje ostatní vlákna, pokud jsou blokována (pthread_cond_broadcast().
Pokud v našem programu čekáme na stisk klávesy v blokovaném režimu prostřednictvím getchar(), byť bez nutnosti potvrzovat přes enter. Proto pokud dojde k chybě čtení, nemáme možnost jak vklákno obsluhy klávesnice odblokovat. V našem případě je to akceptovatelný design choice. Případně můžeme řešit např. načítáním s timeoutem.

Vlákno periodického výpočtu

  • Vlákno periodicky usíná (usleep() na 5 sekund.
  • Po probuzení počítá odhad periody rozsvěcení diody na základě sdílené proměnné s počítadlem rozsvěcení.
  • Pokud je počítadlo nula, nastavuje odhad doby na -1.
  • Po změně stavu může informovat výstupné vlákno.
  • Smyčka periodického usinání končí, pokud je detekován požadavek na ukončení programu (vláken).
V případě požadavku ukončení programu, vlákno reaguje až po probuzení. Proto se ukončení programu může zpozdit až o 5 sekund. Opět je to v našem případě akceptovatelné - Design choice.

Vlákno výstupu

  • Vlákno periodicky vypisuje stavový řádek dle zadání.
  • Po výpisu čeká na signál od ostatních vláken - pthread_cond_wait().
  • V případě požadavku na ukončení, se smyčka periodického čekání na signál od ostatních vláken a aktualizace výpisu ukončuje.

Doporučený postup (s hlavní frontou zpráv)

  • V případě hlavní fronty zpráv je výhodné realizovat komunikaci mezi vlákny výhradně prostřednictvím zpráv, kromě řešení ukončení.
  • To znaméná, že jediné vlákno, které volá nad frontou pop() je hlavní vlákno. Všechna ostatní vlákna volají push().
  • Vlákna zpracovávající asynchroní událost tak nemusejí realizovat konkrétní akci, ale přeposíjají událost (zpráva z Modulu, stisk klávesy, případně i uběhnutí periody 5 sekund) hlavnímu vláknu.
  • Výstup na stdout případně i zasílání zpráv Modulu tak můžeme řešit z hlavního vlákna v obsluze událostí.
  • Proto v obsluze klávesnice po stisku klávesy 'q' dojde k ukončení vlákna (opuštění cyklu načítání stisknuté klávesy), ale ještě předtím vložíme událost do fronty, např. zprávy typu EV_QUIT.
Celkově vede řešení s frontou zpráv typicky na delší kód, ve které bude pravděpodobně jedna velká dlouhá funkce obsluhy událostí z fronty zpráv. Nicméně výhodou je relativní přehlednost a minimální potřeba použití synchronizačních primit pro komunikaci mezi vlákny, což může být výhodné z hlediska rozšiřování programu a případně jeho pozdější údržbě.

Odevzdání a hodnocení

Program pokud možno realizujte na cvičení, nahrajte do odevzdávacího systému. Funkčnost programu ověří učitel na cvičení nebo na dalším cvičení.

Povinné zadání
Název v BRUTE HW9
Odevzdávané soubory prga-hw9-main.c
Argumenty při spuštění žádné
Procvičované oblasti vlákna, pojmenovaná roura
courses/bab36prga/hw/hw9.txt · Last modified: 2024/02/13 23:34 by faiglj