UNIX 7. Deskriptory a proudy Obsah: deskriptor, funkce pro práci se souborem mazání otevřených souborů, důsledky standardní proudy – vznik a použití přesměrování, kolona, filtr © Milan Keršláger http://www.pslib.cz/ke/slajdy 23. 3. 2013 http://creativecommons.org/licenses/by-nc-nd/3.0/
Soubory a proudy již víme, co je soubor a adresář vzniká otázka, jak se s nimi pracuje programátor (uživatel) zná název (+cestu) jádro OS neumí dobře pracovat se jmény souborový deskriptor abstraktní klíč → definuje POSIX pro jazyk C → file handle malé celé číslo (0, 1, 2, 3, …) index do tabulky otevřených souborů
Práce se souborem systémová volání jádra operačního systému řekneme jméno a požadovanou operaci získáme souborový deskriptor dále již používáme jen deskriptor ukončíme práci s deskriptorem → uvolnění knihovní funkce (jazyk C) základní nízkoúrovňové operace fopen, fclose, fread, fwrite, ... interně používají systémové volání vracejí strukturu FILE → datový proud (stream)
open() systémové volání pro otevření souboru příznaky: int open (const char *cesta, int flag,...); příznaky: O_RDONLY → jen pro čtení O_WRONLY → jen pro zápis O_RDWR → pro čtení i zápis doplňující parametry: O_APPEND (jen přidávání), O_CREAT (vytvoření), O_TRUNC (vymazání dat), O_EXCL (chyba, jeli O_CREAT a existuje), O_NONBLOCK (volání selže místo zablokování, je-li bez dat)
Offset v souboru soubor není jako RAM nelze číst libovolný bajt čtení i zápis probíhají z určité pozice jádro si musí pamatovat pozici (offset) počítána v bajtech od začátku další zápis (čtení) automaticky na následující pozici změna pozice pouze pomocí volání lseek() problém s velkými soubory původně 32bitové číslo → 2 GiB moderní systémy implicitně 64bitový
Další systémová volání read() čtení z deskriptoru čte se do bufferu, je dán maximální počet bajtů write() zápis do deskriptoru zápis z bufferu, je dán maximální počet bajtů close() uzavření deskriptoru unlink() smazání zadaného názvu souboru z adresáře
Mazání otevřených souborů běžná praxe v unixových systémech název je jen položka v adresáři (+ odkaz na i- uzel) i-uzel s metadaty existuje nezávisle na jménu využíváno u dočasných souborů fd=open('/tmp/data', O_CREAT | O_EXCL); unlink('/tmp/data'); ... volání funkcí read(), write() close(fd); soubor je založen a vzápětí smazán protože je otevřen, lze zapisovat i číst data v souboru nikdo jiný ale soubor nevidí teprve po uzavření souboru jsou data z disku uvolněna
Důsledky odloženého mazání data jsou uvolněna až později na disku není volné místo ihned po smazání program, ve kterém je otevřen, musí být ukončen aktualizace v paměti je stále starší verze kompatibilita v MS Windows nelze částečně řešeno pomocí O_TEMPORARY tento speciální příznak obsluhuje knihovna způsobuje potíže při psaní přenositelných programů
Aktualizace (1) aktualizace programu program je spuštěn (běží) proběhne aktualizace souboru starý je smazán, nový je vytvořen v RAM stále běží stará verze programu → je nutné program RESTARTOVAT typicky démon poskytující nějakou službu nezbytné u bezpečnostních aktualizací bez restartu může program havarovat snaží se natáhnout komponentu z disku, ale ta je novější lze vyzkoušet u Firefoxu je chyba, že si Firefox o restart sám neřekne
Aktualizace (2) aktualizace knihoven stejný problém jako v předchozím případě bezpečnostní aktualizace knihovny může ohrozit všechny programy, které používají starší verzi na rozdíl od Windows jsou v Linuxu masivně sdíleny řešením je RESTART všech dotčených programů při startu programu je do paměti zavedena nová verze knihoven (podle jejich názvů)
Práce s adresářem opendir() otevření adresáře a nastavení na první položku readdir() vrací přečtenou položku a posune na další položku closedir() uzavření adresáře rewinddir() nastavení na první položku v adresáři ...
Standardní proudy standardní chování unixových systémů proces má při startu otevřeny 3 deskriptory (proudy): 0 – stdin → klávesnice (standardní vstup) 1 – stdout → terminál (standardní výstup) 2 – stderr → terminál (standardní chybový výstup) přípravu deskriptorů zajišťuje VŽDY rodič DOS, Windows – každý program sám → nekompatibility chce-li program číst vstup, čte z deskriptoru 0 chce-li program zapsat výstup, použije 1 nebo 2 využívá se při přesměrování vstupu/výstupu
Původ 0, 1 a 2 nastavení pomocí specializovaného ioctl() při startu systému: první je spuštěn init pro konzoli spustí program getty sdělí programu identifikaci konzole (číslo) getty použije ioctl() pro připojení 0, 1 a 2 getty vypíše login: uživatel zapíše jméno a heslo, getty je zkontroluje getty se změní na shell → exec() dědí deskriptory uživatel zapíše příkaz → potomek dědí deskriptory po odhlášení se ukončí shell a init spustí nové getty
Vznik příkazového řádku getty() jméno + heslo Místo getty může čekat ssh démon (přihlášení ze sítě). exec('/bin/bash') shell ls fork() exec('ls') ls výpis adresáře wait()
Přesměrování vstupu/výstupu využívají se standardní proudy každý program používá deskriptory 0, 1 a 2 démon je zavírá → s uživatelem nekomunikuje rodič pozmění cíl těchto deskriptorů tj. shell je mění podle značek <, >, 2>, >> atd. potomek dědí nastavení deskriptorů tj. spuštěný program (např. ls, cat, grep, ...) potomek používá pro vstup/výstup stále 0, 1 a 2 místo na terminál pak zapisuje program do souboru místo z klávesnice čte z předchozího procesu (kolona) atd.
Přesměrování – BASH > >> < <<X 2>&1 Přesměrování výstupu neexistující soubor je vytvořen, existující je přepsán >> Přesměrování vstupu neexistující soubor je vytvořen, u existujícího přidání za konec < standardní vstup je čten z existujícího souboru <<X Přesměrování vstupu – tzv. „here document“ standardní vstup je čten ze stejného vstupu (nejčastěji ze skriptu) až ke značce X 2>&1 Sloučení stderr (2) se stdout (1) zapisuje se až za přesměrování (ale ev. před znak kolony) Příklady: ls > vystup.txt find /proc > vystup.txt 2>&1 unix2dos < vystup.txt > dos.txt
Realizace přesměrování shell ls > vypis.txt fork() fd = open('vypis.txt', O_CREAT); dup2(fd, stdout); close(fd); exec('ls') ls write(stdout,„ahoj“) výpis adresáře je do stdout, tj. do souboru vypis.txt wait()
Použití standardních proudů standardní deskriptory jsou výhoda programátor se nemusí starat (zajišťuje rodič – shell) využíváno v příkazovém řádku → filtry v GUI skryto na stdout zapisují aplikace ladící informace na stderr zapisují aplikace chybová hlášení typicky však zahazováno chceme-li výstup vidět, spustíme program z přík. řádku Windows 7 → předávání objektů nutno přizpůsobit všechny dotčené programy
Kolona (roura) specifické využití standardních deskriptorů programA | programB stdout z programA jde na stdin programB umožňuje řetězit jednoduché programy umožňuje postupné zpracování vstupních dat využívá koncept jednoúčelových nástrojů a filtrů filtr čte vstup, pozmění ho a zapíše na výstup nedostane-li parametr, používá 0, 1 a 2 téměř všechny unixové nástroje se chovají jako filtry např.: cat, grep, sort, less, awk, sed, …
Realizace kolony (roury) shell grep … | sort Funkce pipe() vytváří dva nové deskriptory, které jsou propojené (zápis do jednoho se objevuje v druhém). Kód je jen naznačen. fork() pipe(); fork() exec('grep'); exec('sort'); grep ... roura sort wait()
Filtr je program, respektující standardní proudy při spuštění bez parametrů: čte z deskriptoru 0 (tj. klávesnice) zapisuje na 1 (stdout) a 2 (stderr) parametry mohou zařídit interní přesměrování: ze souboru nebo do souboru filtr lze použít v koloně kolona propojuje výstup prvního se vstupem druhého filtr „filtruje“ (modifikuje) data → odtud název Příklad: cut -d: -f1 /etc/passwd | less grep ro /etc/passwd | sort