Přidělení paměti založené na zásobníku - Stack-based memory allocation

Typický zásobník, který ukládá místní data a informace o hovorech pro volání vnořených procedur (ne nutně vnořené procedury ). Tento zásobník roste směrem dolů od svého původu. Ukazatel zásobníku ukazuje na aktuální nejvyšší vztažný bod v zásobníku. Operace push zmenší ukazatel a zkopíruje data do zásobníku; operace pop zkopíruje data ze zásobníku a poté zvýší ukazatel. Každá procedura vyvolaná v programu ukládá informace o návratu procedury (žlutě) a lokální data (v jiných barvách) jejich vložením do zásobníku.

Zásobníky ve výpočetních architekturách jsou oblasti paměti, kde jsou data přidávána nebo odebírána způsobem LIFO (last-in-first-out) .

Ve většině moderních počítačových systémů má každé vlákno vyhrazenou oblast paměti označovanou jako jeho zásobník. Když se funkce spustí, může přidat některá ze svých místních stavových dat na začátek zásobníku; když funkce skončí, je zodpovědná za odebrání těchto dat ze zásobníku. Minimálně se zásobník podprocesu používá k uložení umístění zpáteční adresy poskytnuté volajícím, aby bylo možné návratovým příkazům vrátit se na správné místo. Zásobník se často používá k ukládání proměnných pevné délky lokálně k aktuálně aktivním funkcím. Programátoři se dále mohou rozhodnout výslovně použít zásobník k ukládání lokálních dat proměnné délky. Pokud oblast paměti leží na zásobníku podprocesu, říká se, že tato paměť byla alokována v zásobníku, tj . Přidělení paměti založené na zásobníku .

Výhody a nevýhody

Protože jsou data přidávána a odebírána způsobem poslední v pořadí, je přidělování paměti založené na zásobníku velmi jednoduché a obvykle mnohem rychlejší než přidělování paměti založené na haldě (známé také jako dynamické přidělování paměti ) obvykle přidělované prostřednictvím malloc. Další funkcí je, že paměť v zásobníku se automaticky a velmi efektivně obnovuje po ukončení funkce, což může být výhodné pro programátora, pokud již data nejsou požadována. (Totéž platí pro longjmp, pokud se přesunul do bodu před voláním, které se allocastalo.) Pokud je však třeba data uchovávat v nějaké formě, musí být před ukončením funkce zkopírována ze zásobníku na hromadu. Alokace založená na zásobníku je proto vhodná pro dočasná data nebo data, která již po ukončení aktuální funkce nejsou nutná.

Velikost zásobníku přiřazená vláknu může být na některých malých CPU tak malá, jako jen několik bajtů. Přidělení většího množství paměti v zásobníku, než je k dispozici, může mít za následek selhání kvůli přetečení zásobníku . To je také důvod, proč allocaje obvykle zabráněno vložení funkcí, které používají : pokud by byla taková funkce vložena do smyčky, volající by trpěl neočekávaným nárůstem využití zásobníku, což by přetečení znamenalo mnohem větší pravděpodobnost.

Alokace založená na zásobníku může také způsobit menší problémy s výkonem: vede k rámcům zásobníku proměnné velikosti, takže je třeba spravovat ukazatele zásobníku i rámce (u rámců zásobníku pevné velikosti je jeden z nich nadbytečný). To je obvykle mnohem méně nákladné než volání malloca freestejně. Zejména pokud aktuální funkce obsahuje jak volání aloky, tak bloky obsahující lokální data s proměnnou délkou, pak dochází ke konfliktu mezi pokusy aloky o zvýšení aktuálního rámce zásobníku, dokud aktuální funkce neskončí oproti potřebě kompilátoru umístit lokální proměnné proměnné délky do stejné umístění v rámečku zásobníku. Tento konflikt je obvykle vyřešen vytvořením samostatného řetězce haldy úložiště pro každé volání alokace (viz: https://code.woboq.org/gcc/libiberty/alloca.c.html ). Řetězec zaznamenává hloubku zásobníku, při které dochází k každé alokaci, následné volání alokace v jakékoli funkci ořízne tento řetězec až na aktuální hloubku zásobníku, aby nakonec (ale ne hned) uvolnilo jakékoli úložiště v tomto řetězci. Volání alokace s nulovým argumentem lze také použít ke spuštění uvolnění paměti bez přidělení další takové paměti. V důsledku tohoto konfliktu mezi alokou a lokálním úložištěm proměnných nemusí být používání aloky o nic efektivnější než používání malloc.

Rozhraní systému

Mnoho unixových systémů a Microsoft Windows implementují funkci, která vyžaduje allocadynamické přidělování paměti zásobníku podobným způsobem jako na základě haldy malloc. Kompilátor jej obvykle převede na vložené pokyny manipulující s ukazatelem zásobníku, podobně jako se zachází s poli s proměnnou délkou . Přestože není nutné paměť výslovně uvolňovat, existuje riziko nedefinovaného chování kvůli přetečení zásobníku. Tato funkce byla na unixových systémech přítomna již v 32/V (1978), ale není součástí standardu C ani standardu POSIX .

V systému Microsoft Windows existuje bezpečnější verze allocavolání _malloca, která hlásí chyby. Vyžaduje použití _freea. gnulib poskytuje ekvivalentní rozhraní, i když místo vyvolání výjimky SEH při přetečení deleguje, mallockdyž je detekována nadměrná velikost. Podobnou funkci lze emulovat pomocí ručního účetnictví a kontroly velikosti, například při použití alloca_accountv glibc.

Některé rodiny procesorů, například x86 , mají speciální pokyny pro manipulaci se zásobníkem aktuálně prováděného vlákna. Jiné rodiny procesorů, včetně PowerPC a MIPS , nemají explicitní podporu zásobníku, ale místo toho se spoléhají na konvenci a delegují správu zásobníku na binární rozhraní aplikace (ABI) operačního systému .

Viz také

Reference