Reentrancy (výpočetní) - Reentrancy (computing)
![]() | Tento článek má několik problémů. Prosím pomozte vylepši to nebo diskutovat o těchto problémech na internetu diskusní stránka. (Zjistěte, jak a kdy tyto zprávy ze šablony odebrat) (Zjistěte, jak a kdy odstranit tuto zprávu šablony)
|
v výpočetní, a počítačový program nebo podprogram je nazýván reentrant pokud může více vyvolání bezpečně běžet souběžně na jednom procesorovém systému, kde může být procedura opětovného zadání přerušena uprostřed jejího provádění a poté bezpečně znovu volána („znovu zadána“) před úplným provedením jejích předchozích vyvolání. Přerušení může být způsobeno vnitřní akcí, jako je skok nebo volání, nebo vnější akcí, jako je přerušit nebo signál na rozdíl od rekurze, kde nová vyvolání mohou být způsobena pouze interním voláním.
Tato definice pochází z multiprogramovacích prostředí, kde by tok řízení mohl být přerušen přerušit a převedeny do přerušit servisní rutinu (ISR) nebo podprogram „handler“. Jakýkoli podprogram používaný obslužnou rutinou, který by mohl být spuštěn při spuštění přerušení, by měl být znovu zadán. Často jsou to podprogramy přístupné přes operační systém jádro nejsou reentrantní. Rutiny přerušení služby jsou proto omezené v akcích, které mohou provádět; například jsou obvykle omezeni v přístupu k systému souborů a někdy dokonce v přidělování paměti.
Tato definice reentrancy se liší od definice bezpečnost nití v prostředí s více vlákny. Reentrantní podprogram může dosáhnout bezpečnosti závitů,[1] ale být reentrantem sám o sobě nemusí stačit, aby byl bezpečný ve všech situacích. Naopak kód bezpečný pro vlákna nemusí nutně být znovu zadán (příklady viz níže).
Mezi další termíny používané pro programy reentrant patří „čistý postup“[2] nebo „sdílený kód“.[3] Reentrantní podprogramy jsou někdy v referenčním materiálu označeny jako „signálně bezpečné“.[4]
Pozadí
Reentrancy není totéž jako idempotence, ve kterém může být funkce volána vícekrát, a přesto generovat přesně stejný výstup, jako kdyby byla volána pouze jednou. Obecně řečeno, funkce produkuje výstupní data na základě některých vstupních dat (obě jsou obecně volitelná). Ke sdíleným datům může kdykoli přistupovat libovolná funkce. Pokud lze libovolnou funkcí změnit data (a nikdo tyto změny nezachytí), neexistuje žádná záruka pro ty, kteří sdílejí jeden údaj, že tento údaj je stejný jako kdykoli předtím.
Data mají charakteristiku nazvanou rozsah, který popisuje, kde v programu mohou být data použita. Rozsah dat je buď globální (mimo rozsah jakékoli funkce a v neurčitém rozsahu) nebo místní (vytvořeno pokaždé, když je funkce zavolána a zničena při ukončení).
Místní data nesdílí žádné rutiny, opětovné zadávání nebo ne; proto to nemá vliv na opětovný vstup. Globální data jsou definována mimo funkce a lze k nim přistupovat více než jednou funkcí, ať už ve formě globální proměnné (data sdílená mezi všemi funkcemi), nebo jako statické proměnné (data sdílená všemi funkcemi se stejným názvem). v objektově orientované programování, globální data jsou definována v rozsahu třídy a mohou být soukromá, takže jsou přístupná pouze funkcím dané třídy. Existuje také koncept proměnné instance, kde je proměnná třídy vázána na instanci třídy. Z těchto důvodů je v objektově orientovaném programování tento rozdíl obvykle vyhrazen pro data přístupná mimo třídu (veřejná) a pro data nezávislá na instancích třídy (statická).
Reentrancy se liší od, ale úzce souvisí s bezpečnost nití. Funkce může být bezpečné pro vlákna a stále znovu. Například funkce by mohla být zabalena všude kolem s mutex (což se vyhne problémům v prostředích s více vlákny), ale pokud by byla tato funkce použita v rutině přerušení služby, mohla by hladovět čekat na první spuštění, aby uvolnila mutex. Klíčem k zabránění nejasnostem je, že reentrant odkazuje pouze na jeden provádění vlákna. Je to koncept z doby, kdy neexistovaly žádné multitaskingové operační systémy.
Pravidla pro opětovné přijetí
- Reentrantní kód nemusí obsahovat žádná statická ani globální nekonstantní data.
- Funkce reentrantu mohou pracovat s globálními daty. Například rutina služby přerušení reentrantního přerušení by mohla získat část hardwarového stavu, se kterou bude pracovat (např. Vyrovnávací paměť pro čtení sériového portu), která je nejen globální, ale i nestálá. Typické použití statických proměnných a globálních dat se přesto nedoporučuje, pouze v tom smyslu atomový read-modify-write v těchto proměnných by měly být použity instrukce (nemělo by být možné, aby při provádění takové instrukce přišlo přerušení nebo signál). Všimněte si, že v C není zaručeno, že ani čtení nebo zápis budou atomové; může být rozdělena do několika čtení nebo zápisů.[5] Standard C a SUSv3 poskytují
sig_atomic_t
za tímto účelem, i když se zárukami pouze pro jednoduché čtení a zápis, nikoli pro zvyšování nebo snižování.[6] Složitější atomové operace jsou k dispozici v C11, který stanovístdatomic.h
. - Reentrantní kód nemusí upravit sám.
- Operační systém může procesu umožnit upravit svůj kód. Existuje několik důvodů (např. blitting grafika rychle), ale to by způsobilo problém s reentrancy, protože kód nemusí být příště stejný.
- Může se však upravit, pokud se nachází ve své vlastní jedinečné paměti. To znamená, že pokud každé nové vyvolání používá jiné fyzické umístění strojového kódu, kde je vytvořena kopie původního kódu, neovlivní to další vyvolání, i když se během provádění daného konkrétního vyvolání (vlákna) sám upraví.
- Kód nového odesílatele nemusí volat neregistrovaného počítačové programy nebo rutiny.
- Více úrovní uživatele, objektu nebo procesu přednost nebo multiprocesing obvykle komplikují kontrolu reentrantního kódu. Je důležité sledovat jakýkoli přístup nebo vedlejší účinky, ke kterým dochází uvnitř rutiny určené k opětovnému vstupu.
Reentrancy podprogramu, který pracuje na zdrojích operačního systému nebo nelokálních datech, závisí na atomicita příslušných operací. Například pokud podprogram upraví 64bitovou globální proměnnou na 32bitovém počítači, lze operaci rozdělit na dvě 32bitové operace, a pokud je tedy podprogram při provádění přerušen a znovu vyvolán obsluhou přerušení , globální proměnná může být ve stavu, kdy bylo aktualizováno pouze 32 bitů. Programovací jazyk může poskytovat záruky atomicity za přerušení způsobené interní akcí, jako je skok nebo volání. Pak funkce F
ve výrazu jako (globální: = 1) + (f ())
, kde pořadí vyhodnocení dílčích výrazů může být v programovacím jazyce libovolné, uvidí globální proměnnou nastavenou na 1 nebo na její předchozí hodnotu, ale ne v mezilehlém stavu, kde byla aktualizována pouze část. (To se může stát v C, protože výraz nemá bod posloupnosti.) Operační systém může poskytovat záruky atomicity pro signály, například systémové volání přerušené signálem, který nemá částečný účinek. Hardware procesoru může poskytovat záruky atomicity pro přerušení, například přerušené instrukce procesoru, které nemají částečné účinky.
Příklady
Pro ilustraci reentrancy používá tento článek jako příklad a C užitková funkce, vyměnit ()
, který bere dva ukazatele a transponuje jejich hodnoty, a rutinu zpracování přerušení, která také volá funkci swap.
Ani reentrantní, ani bezpečné pro vlákna
Toto je příklad swapové funkce, která se nepodařilo reentrantovat nebo bezpečně používat vlákna. Protože tmp
proměnná je globálně sdílená, bez serializace, mezi souběžnými instancemi funkce může jedna instance interferovat s daty, na která se spoléhá jiná. Proto by neměl být používán v rutině přerušení služby isr ()
:
int tmp;prázdnota vyměnit(int* X, int* y){ tmp = *X; *X = *y; / * Hardwarové přerušení by zde mohlo vyvolat isr (). * / *y = tmp; }prázdnota isr(){ int X = 1, y = 2; vyměnit(&X, &y);}
Bezpečné pro vlákna, ale ne reentrantní
Funkce vyměnit ()
v předchozím příkladu lze provést bezpečným pro vlákna vytvořením tmp
místní vlákno. Stále se to nedaří znovu provést, a to bude i nadále způsobovat problémy, pokud isr ()
je volán ve stejném kontextu jako vlákno, které se již provádí vyměnit ()
:
_Thread_local int tmp;prázdnota vyměnit(int* X, int* y){ tmp = *X; *X = *y; / * Hardwarové přerušení by zde mohlo vyvolat isr (). * / *y = tmp; }prázdnota isr(){ int X = 1, y = 2; vyměnit(&X, &y);}
Znovu zadejte, ale není bezpečný pro vlákna
Následující (poněkud nepřirozená) modifikace funkce swapu, která je opatrná, aby byla globální data v době jejich ukončení ponechána v konzistentním stavu, je reentrantní; není však bezpečný pro vlákna, protože nejsou použity žádné zámky, lze jej kdykoli přerušit:
int tmp;prázdnota vyměnit(int* X, int* y){ / * Uložit globální proměnnou. * / int s; s = tmp; tmp = *X; *X = *y; *y = tmp; / * Hardwarové přerušení by zde mohlo vyvolat isr (). * / / * Obnovit globální proměnnou. * / tmp = s;}prázdnota isr(){ int X = 1, y = 2; vyměnit(&X, &y);}
Reentrantní a bezpečné pro vlákna
Implementace vyměnit ()
který přiděluje tmp
na zásobník místo globálně a to se volá pouze s nesdílenými proměnnými jako parametry[A] je bezpečný pro vlákna i reentrantní. Bezpečné pro podprocesy, protože zásobník je pro vlákno místní a funkce působící pouze na místní data vždy vytvoří očekávaný výsledek. Neexistuje žádný přístup ke sdíleným datům, proto neexistuje žádný datový závod.
prázdnota vyměnit(int* X, int* y){ int tmp; tmp = *X; *X = *y; *y = tmp; / * Hardwarové přerušení by zde mohlo vyvolat isr (). * /}prázdnota isr(){ int X = 1, y = 2; vyměnit(&X, &y);}
Znovuobjevte obsluhu přerušení
Obsluha přerušení reentrant je obsluha přerušení že znovu povolí přerušení na začátku obsluhy přerušení. To může snížit přerušení latence.[7] Obecně se při programování rutin služby přerušení doporučuje co nejdříve znovu povolit přerušení v obslužné rutině přerušení. Tato praxe pomáhá zabránit ztrátě přerušení.[8]
Další příklady
V následujícím kódu ani jeden F
ani G
funkce jsou reentrantní.
int proti = 1;int F(){ proti += 2; vrátit se proti;}int G(){ vrátit se F() + 2;}
Ve výše uvedeném F()
závisí na nekonstantní globální proměnné proti
; tedy pokud F()
je během provádění přerušen ISR, který upravuje proti
, pak se vraťte do F()
vrátí nesprávnou hodnotu proti
. Hodnota proti
a proto návratová hodnota F
, nelze s jistotou předvídat: budou se lišit v závislosti na tom, zda došlo k úpravě přerušení proti
v době F
poprava. Proto, F
není reentrant. Ani to není G
, protože volá F
, který není reentrantní.
Tyto mírně pozměněné verze jsou reentrant:
int F(int i){ vrátit se i + 2;}int G(int i){ vrátit se F(i) + 2;}
V následujícím textu je funkce bezpečná pro vlákna, ale ne znovu zadaná:
int funkce(){ mutex_lock(); // ... // tělo funkce // ... mutex_unlock();}
Ve výše uvedeném funkce()
lze bez problémů volat různými vlákny. Pokud se ale funkce používá v reentrantním obslužném programu přerušení a uvnitř funkce vznikne druhé přerušení, bude druhá rutina navždy viset. Protože servis přerušení může deaktivovat další přerušení, mohl by utrpět celý systém.
Poznámky
- ^ Pokud se isr () nazývá swap () s jednou nebo dvěma globálními proměnnými jako parametry, pak by swap () nebyl znovu zadán
Viz také
Reference
- ^ Kerrisk 2010, str.657.
- ^ Barron, David William (1968) [1967]. „3.2. Metody ad hoc“. Napsáno v Cambridge ve Velké Británii. v Gill, Stanley (vyd.). Rekurzivní techniky v programování. Macdonald Computer Monographs (1. vyd.). Londýn, Velká Británie: Macdonald & Co. (Publishers) Ltd. p.35. SBN 356-02201-3. (viii + 64 stránek)
- ^ Ralston 2000, str. 1514–1515.
- ^ "pthread_cond_init () - inicializovat proměnnou podmínky". IBM Knowledge Center. Citováno 2019-10-05.
- ^ Preshing, Jeff (18.06.2013). „Atomový vs. ne-atomový provoz“. Předvolba programování. Archivováno od originálu dne 2014-12-03. Citováno 2018-04-24.
- ^ Kerrisk 2010, str.428.
- ^ Sloss a kol. 2004, str.342.
- ^ Regehr, Johne (2006). „Bezpečné a strukturované používání přerušení v reálném čase a vestavěném softwaru“ (PDF). Příručka real-time a vestavěných systémů. CRC Press. Archivováno (PDF) od originálu 2007-08-24 - prostřednictvím autorského webu na University of Utah School of Computing.
Citované práce
- Kerrisk, Michael (2010). Programovací rozhraní Linuxu. Žádný lis na škrob.CS1 maint: ref = harv (odkaz)
- Ralston, Anthony, vyd. (2000). "Program reentrantů". Encyclopedia of Computer Science (4. vydání). Nature Publishing Group.CS1 maint: ref = harv (odkaz)
- Sloss, Andrew N .; Symes, Dominic; Wrighte, Chrisi; Rayfield, John (2004). Příručka pro vývojáře systému ARM. Nakladatelé Morgan Kaufmann. ISBN 9780080490496.CS1 maint: ref = harv (odkaz)
Další čtení
- Chen, Raymond (2004-06-29). „Rozdíl mezi bezpečností vláken a opakovaným vstupem do styku“. Stará nová věc. Microsoft Developer Network. Archivováno od originálu na 2018-04-24. Citováno 2018-04-24.
- Ganssle, Jack (2001-03-15). „Úvod do reentrancy“. Embedded.com. Archivováno od originálu dne 2013-01-21. Citováno 2018-04-24.
- IBM (2018). „Obecné programovací koncepty“ (PDF). Příručka k systému AIX verze 7.2. p. 636–641. Citováno 2018-04-24.
- Jha, Dipak (2005-01-20). „Použijte funkce reentrantu pro bezpečnější zpracování signálu“. IBM DeveloperWorks. Archivováno z původního dne 2014-07-07. Citováno 2018-04-24.