GNU/Linux >> Znalost Linux >  >> Linux

Otázky týkající se putenv() a setenv()

  • [The] putenv(char *string); [...] hovor se zdá být fatálně chybný.

Ano, je to fatální chyba. Byl zachován v POSIX (1988), protože to byl stav techniky. setenv() mechanismus dorazil později. Oprava: Standard POSIX 1990 říká v §B.4.6.1 „Další funkce putenv() a clearenv() byly zváženy, ale zamítnuty." Single Unix Specification (SUS) verze 2 z roku 1997 uvádí putenv() ale ne setenv() nebo unsetenv() . Další revize (2004) definovala obě setenv() a unsetenv() také.

Protože nekopíruje předaný řetězec, nemůžete jej volat s místním a neexistuje žádná záruka, že řetězec přidělený haldě nebude přepsán nebo náhodně odstraněn.

Máte pravdu, že místní proměnná je téměř vždy špatná volba pro předání do putenv() — výjimky jsou nejasné do té míry, že téměř neexistují. Pokud je řetězec alokován na haldě (s malloc() et al), musíte zajistit, aby jej váš kód neměnil. Pokud ano, zároveň mění prostředí.

Navíc (ačkoli jsem to netestoval), protože jedním z použití proměnných prostředí je předávání hodnot do prostředí dítěte, zdá se to zbytečné, pokud dítě volá jednu z exec*() funkcí. Mýlím se v tom?

exec*() funkce vytvoří kopii prostředí a předají ji prováděnému procesu. V tom není žádný problém.

Manuálová stránka Linuxu naznačuje, že glibc 2.0-2.1.1 opustil výše uvedené chování a začal kopírovat řetězec, ale to vedlo k úniku paměti, který byl opraven v glibc 2.1.2. Není mi jasné, co byl tento únik paměti nebo jak byl opraven.

K úniku paměti dochází, protože jakmile jste zavolali putenv() s řetězcem nemůžete tento řetězec znovu použít k žádnému účelu, protože nemůžete zjistit, zda se stále používá, i když byste mohli upravit hodnotu přepsáním (s neurčitými výsledky, pokud změníte název na název proměnné prostředí nalezené na jiné pozici v prostředí). Pokud tedy máte přidělený prostor, klasický putenv() unikne, pokud proměnnou znovu změníte. Když putenv() začal kopírovat data, alokované proměnné se staly bez odkazů, protože putenv() již neuchovával odkaz na argument, ale uživatel očekával, že na něj bude odkazovat prostředí, takže došlo k úniku paměti. Nejsem si jistý, jaká oprava byla – ze 3/4 bych očekával, že se vrátí ke starému chování.

setenv() zkopíruje řetězec, ale nevím přesně, jak to funguje. Prostor pro prostředí je přidělen při načítání procesu, ale je opraven.

Prostor původního prostředí je pevný; když ho začnete upravovat, změní se pravidla. I s putenv() , původní prostředí je upraveno a mohlo by se zvětšit v důsledku přidání nových proměnných nebo v důsledku změny existujících proměnných tak, aby měly delší hodnoty.

Funguje zde nějaká (libovolná?) konvence? Například přidělit více slotů v poli ukazatelů env řetězců, než se aktuálně používá, a podle potřeby přesunout nulový ukončovací ukazatel dolů?

To je to, co setenv() mechanismus pravděpodobně udělá. (Globální) proměnná environ ukazuje na začátek pole ukazatelů na proměnné prostředí. Pokud ukazuje na jeden blok paměti v jednu chvíli a na jiný blok v jinou dobu, pak se prostředí přepne, stejně tak.

Je paměť pro nový (zkopírovaný) řetězec alokována v adresovém prostoru samotného prostředí, a pokud je příliš velká, abyste se vešli, dostanete pouze ENOMEM?

No, ano, mohli byste získat ENOMEM, ale museli byste se hodně snažit. A pokud prostředí příliš zvětšíte, může se stát, že nebudete moci správně spouštět další programy – buď bude prostředí zkráceno, nebo operace exec selže.

Vzhledem k výše uvedeným problémům, existuje nějaký důvod upřednostňovat putenv() před setenv()?

  • Použijte setenv() v novém kódu.
  • Aktualizujte starý kód na setenv() , ale nedělejte z toho nejvyšší prioritu.
  • Nepoužívejte putenv() v novém kódu.

Neexistuje žádný speciální prostor pro „prostředí“ – setenv pouze dynamicky přiděluje prostor pro řetězce (s malloc například), jak byste to dělali normálně. Protože prostředí neobsahuje žádný údaj o tom, odkud každý řetězec v něm pochází, není možné pro setenv nebo unsetenv pro uvolnění místa, které mohlo být dynamicky přiděleno předchozími voláními setenv.

"Protože nekopíruje předaný řetězec, nemůžete jej volat s místním a neexistuje žádná záruka, že řetězec přidělený haldě nebude přepsán nebo náhodně odstraněn." Účelem putenv je zajistit, že pokud máte řetězec alokovaný na haldě, je možné jej smazat úmyslně . To je to, co text odůvodnění znamená „jedinou dostupnou funkci, kterou lze přidat do prostředí bez povolení úniků paměti“. A ano, můžete to volat pomocí local, stačí odstranit řetězec z prostředí (putenv("FOO=") nebo unsetenv), než se vrátíte z funkce.

Jde o to, že pomocí putenv je proces odstraňování řetězce z prostředí zcela deterministický. Zatímco setenv u některých stávajících implementací upraví stávající řetězec v prostředí, pokud je nová hodnota kratší (aby se zabránilo vždy únik paměti), a protože vytvořil kopii, když jste zavolali setenv, nemáte kontrolu nad původně dynamicky alokovaným řetězcem, takže jej nemůžete uvolnit, když je odstraněn.

Mezitím setenv sám (nebo unsetenv) nemůže uvolnit předchozí řetězec, protože – i když ignorujeme putenv – řetězec mohl pocházet z původního prostředí místo toho, aby byl alokován předchozím vyvoláním setenv.

(Celá tato odpověď předpokládá správně implementovaný putenv, tj. ne ten v glibc 2.0-2.1.1, který jste zmínil.)


Linux
  1. 25 nejčastějších otázek a odpovědí v rozhovoru pro Linux

  2. 20 Postfix Interview Otázky a odpovědi

  3. BIND – DNS Server Interview Otázky a odpovědi

  1. 30 nejlepších otázek a odpovědí v rámci rozhovoru s OpenStack

  2. O Mem a Vmem?

  3. Kdo nastavuje proměnné prostředí $user a $username?

  1. 10 zajímavých a zábavných faktů o Linuxu

  2. Tipy a triky pro proměnné prostředí Linuxu

  3. Zbarvení vašeho terminálu a prostředí Shell?