Vytváření procesů v C a překlad make
Domácí příprava
Předpokládáme, že máte základní znalosti jazyka C a víte, jak funguje překlad ze zdrojových kódů jazyka C do binární spustitelné aplikace (v obecném případě, kdy je zdrojových souborů více).
Dále byste měli mít alespoň minimální povědomí o použití překladače gcc
a
jeho základních parametrech.
Nastudujte si použití nástroje make
pro překlad programu v jazyku C/C++:
make.
Dále je pro absolvování cvičení nutné mít přehled o systémových voláních fork, pipe, dup, open, kill, wait a execve, tzn. měli byste vědět jak vzniká nový proces a jak lze přesměrovat standardní vstup a výstup. Potřebné informace se dozvíte na některé z předchozích přednášek.
Zadání úlohy
Vytvořte v jazyce C, C++ nebo Rust program forkpipe
, který:
-
Vytvoří dva procesy (potomky) voláním funkce
fork
. Prvně vytvořený proces budeme níže nazývat GEN, druhý NSD. Původní (hlavní) proces bude označován jako MAIN. (POZOR, záleží na pořadí vytvoření potomků). -
Tyto potomky propojí rourou (funkce
pipe
adup2
) tak, aby standardní výstup GEN byl napojen na standardní vstup NSD. -
Poté počká 5 sekund pomocí volání
sleep(5)
. -
Poté pošle procesu
GEN
signálSIGTERM
, který způsobí ukončení obou potomků.GEN skončí díky obsluze signálu – viz níže, NSD by pak měl skončit automaticky také, neboť při ukončení GEN OS uzavře rouru a NSD je naprogramován tak, že skončí při uzavření standardního vstupu.
-
Následně počká na ukončení procesů GEN a NSD (např. pomocí funkce
wait
). -
Pokud libovolný z potomků skončil chybou (návratový kód
!= 0
), vypíše na standardní výstup řádku “ERROR
” a skončí s návratovým kódem1
, v opačném případě vypíše tamtéž řádku “OK
” a skončí s kódem0
. -
Pokud libovolné systémové volání vrátí chybu, aktuální proces okamžitě skončí s návratovým kódem
2
. -
GEN bude na standardní výstup vypisovat řádky obsahující dvě mezerou oddělená náhodná čísla vygenerovaná funkcí
rand()
(např.printf(“%d %d\n”, rand(), rand())
).Pozor, při generování příliš vysokých čísel může program nsd počítat i několik sekund. Můžete předpokládat, že při testech v BRUTE nevrátí funkce
rand()
větší číslo než4095
a váš program urychlit použitímrand() % 4096
místo samotnéhorand()
. -
Mezi výpisem řádek bude GEN čekat jednu sekundu pomocí volání
sleep(1)
. -
GEN bude reagovat na signál
SIGTERM
. Při zaslání tohoto signálu GEN vypíše na standardní chybový výstup řádku “GEN TERMINATED
” a skončí s návratovým kódem0
. -
NSD zavolá po vytvoření funkci
execl
, aby začal vykonávat programnsd
(viz níže). Žádný jiný proces by nemělexecl
volat.
Dále vytvořte Makefile
, který:
- Při spuštění příkazu
make
bez parametrů zkompiluje váš programforkpipe
a programnsd
pro výpočet největšího společného dělitele, jehož zdrojové kódy si stáhněte z nsd.tgz. Obě binárky program umístí do stejného adresáře jako souborMakefile
. - Při změně libovolného zdrojového souboru
make
překompiluje pouze soubory, které jsou z daného souboru generovány (ať přímo či nepřímo). Ostatní generované soubory měněny nebudou. - Překladači jazyka C/C++ vždy předá volbu
-Wall
, která způsobí výpis užitečných varování, a dále volby v proměnnéEXTRA_CFLAGS
, která je ve výchozím stavu prázdná.
Poznámky k implementaci
-
Rouru musíte vytvořit dříve než potomky, abyste je s ní mohli propojit.
-
Po vytvoření potomků rodičovský proces rouru nepotřebuje a měl by ji uzavřít. Pokud ji neuzavře, NSD neskončí automaticky po ukončení GEN, jak je popsáno výše, protože roura bude stále otevřená.
-
Uzavírejte důsledně všechny file descriptory, které již nebudete potřebovat. Předejdete tím záludným chybám.
-
Z funkce obsluhující signál (tzv. signal handler) není možné volat libovolnou funkci. Viz man signal-safety. Zejména by se nemělo používat “buffered I/O”, tedy např. funkce
printf
. Pokud nepodporovanou funkci zavoláte, program může (ale nemusí) zhavarovat (jedná se o tzv. chybu souběhu neboli “race condition”). -
Pro ladění programu se vám můžete hodit příkaz
strace -f
. Vyzkoušejte také argumenty-e process
,-e file
,-e trace=dup2
, apod. -
Odevzdávejte všechny soubory zabalené v archivu. Můžete použít příkaz
tar czf osy04.tgz Makefile *.[ch]
tar czf osy04.tgz Makefile *.[ch] Cargo.toml src
-
Pokud program implementujete v Rustu můžete používat následující Crates a to tak, že v
Cargo.toml
uvedete:[dependencies] nix = { version = "0.29", features = ["signal", "fs"] } libc = "0.2" anyhow = "1.0" lazy_static = "1.5"
Ne všechny poskytnuté crates je nutné využít, správné řešení v Rustu může jít napsat i bez nich. Dostupné verze crates jsou dané tím, jaké verze byly použity při vytváření obrazu pro BRUTE a mohou být starší než jaké budete používáte lokálně.
Program překládejte pomocí
cargo build --offline
(v Makefile).Náhodná čísla generujte pomocí
libc::rand()
. Standardně se v Rustu používá craterand
, v rámci AE ale používáme vlastní generátor náhodných čísel, aby byly výsledky reprodukovatelné, a ten aktuálně funguje pouze slibc::rand()
.
Aplikace nsd
Zdrojové kódy aplikace nsd
se skládají ze souborů nsd_main.c
, nsd.c
a
nd.c
a hlavičkových souborů nsd.h
a nd.h
. Jejich přeložením vytvoříte
binární soubor ‘nsd
’.
Zdrojové soubory naleznete v archivu nsd.tgz.
Ukázkový kód
Jak vytvořit proces a rouru se můžete inspirovat ukázkovým kódem z manuálové stránky systémového volání pipe.
Domácí příprava na další cvičení
Pro další cvičení budete potřebovat vědět
- co jsou vlákna,
- jaké problémy mohou nastávat při paralelních běhů vláken,
- jaké synchronizační prostředky máme k dispozici a
- jak je vytvoříme v jazyce C s využitím knihovny pthread.
Potřebné informace byste měli získat na přednáškách, případně se podívejte na