GNU/Linux >> Linux Esercitazione >  >> Linux

Domande su putenv() e setenv()

  • [Il] putenv(char *string); [...] la chiamata sembra fatalmente errata.

Sì, è fatalmente difettoso. È stato conservato in POSIX (1988) perché quella era la tecnica anteriore. Il setenv() meccanismo arrivato dopo. Correzione: Lo standard POSIX 1990 dice in §B.4.6.1 "Funzioni aggiuntive putenv() e clearenv() sono stati considerati ma respinti". La versione 2 della Single Unix Specification (SUS) del 1997 ne elenca putenv() ma non setenv() o unsetenv() . La revisione successiva (2004) ha definito entrambi setenv() e unsetenv() anche.

Poiché non copia la stringa passata, non è possibile chiamarla con un local e non vi è alcuna garanzia che una stringa allocata nell'heap non venga sovrascritta o eliminata accidentalmente.

Hai ragione che una variabile locale è quasi invariabilmente una cattiva scelta da passare a putenv() — le eccezioni sono oscure al punto da quasi non esistere. Se la stringa è allocata nell'heap (con malloc() et al), devi assicurarti che il tuo codice non lo modifichi. Se lo fa, sta modificando l'ambiente allo stesso tempo.

Inoltre (anche se non l'ho testato), poiché un uso delle variabili d'ambiente è passare valori all'ambiente del bambino, questo sembra inutile se il bambino chiama uno dei exec*() funzioni. Mi sbaglio?

Il exec*() le funzioni creano una copia dell'ambiente e la passano al processo eseguito. Non ci sono problemi.

La pagina man di Linux indica che glibc 2.0-2.1.1 ha abbandonato il comportamento precedente e ha iniziato a copiare la stringa, ma ciò ha portato a una perdita di memoria che è stata corretta in glibc 2.1.2. Non mi è chiaro cosa fosse questa perdita di memoria o come sia stata riparata.

La perdita di memoria si verifica perché una volta chiamato putenv() con una stringa, non puoi usare di nuovo quella stringa per nessuno scopo perché non puoi dire se è ancora in uso, sebbene tu possa modificare il valore sovrascrivendolo (con risultati indeterminati se cambi il nome in quello di una variabile d'ambiente trovato in un'altra posizione nell'ambiente). Quindi, se hai allocato spazio, il classico putenv() lo perde se cambi di nuovo la variabile. Quando putenv() ha iniziato a copiare i dati, le variabili allocate sono diventate senza riferimento perché putenv() non manteneva più un riferimento all'argomento, ma l'utente si aspettava che l'ambiente vi facesse riferimento, quindi la memoria è trapelata. Non sono sicuro di quale sia stata la correzione:mi sarei aspettato per 3/4 che tornasse al vecchio comportamento.

setenv() copia la stringa ma non so esattamente come funziona. Lo spazio per l'ambiente viene allocato quando il processo viene caricato ma è fisso.

Lo spazio dell'ambiente originale è fisso; quando inizi a modificarlo, le regole cambiano. Anche con putenv() , l'ambiente originale viene modificato e potrebbe crescere come risultato dell'aggiunta di nuove variabili o come risultato della modifica delle variabili esistenti per avere valori più lunghi.

C'è qualche convenzione (arbitraria?) al lavoro qui? Ad esempio, allocare più slot nell'array di puntatori della stringa env rispetto a quelli attualmente utilizzati e spostare il puntatore di terminazione null verso il basso secondo necessità?

Questo è ciò che il setenv() meccanismo è probabile che faccia. La variabile (globale) environ punta all'inizio dell'array di puntatori alle variabili d'ambiente. Se punta a un blocco di memoria alla volta e a un blocco diverso in un momento diverso, allora l'ambiente cambia, proprio così.

La memoria per la nuova stringa (copiata) è allocata nello spazio degli indirizzi dell'ambiente stesso e se è troppo grande per adattarsi ottieni semplicemente ENOMEM?

Beh, sì, potresti ottenere ENOMEM, ma dovresti sforzarti piuttosto. E se espandi troppo l'ambiente, potresti non essere in grado di eseguire correttamente altri programmi:l'ambiente verrà troncato o l'operazione exec fallirà.

Considerando i problemi di cui sopra, c'è qualche motivo per preferire putenv() a setenv()?

  • Usa setenv() nel nuovo codice.
  • Aggiorna il vecchio codice per utilizzare setenv() , ma non farne una priorità assoluta.
  • Non utilizzare putenv() nel nuovo codice.

Non c'è uno spazio speciale "l'ambiente" - setenv si limita ad allocare dinamicamente lo spazio per le stringhe (con malloc per esempio) come faresti normalmente. Poiché l'ambiente non contiene alcuna indicazione della provenienza di ogni stringa in esso contenuta, è impossibile per setenv o unsetenv per liberare qualsiasi spazio che potrebbe essere stato allocato dinamicamente da precedenti chiamate a setenv.

"Poiché non copia la stringa passata, non è possibile chiamarla con un local e non vi è alcuna garanzia che una stringa allocata nell'heap non venga sovrascritta o eliminata accidentalmente." Lo scopo di putenv è assicurarsi che se si dispone di una stringa allocata nell'heap sia possibile eliminarla di proposito . Questo è ciò che il testo logico intende per "l'unica funzione disponibile da aggiungere all'ambiente senza consentire perdite di memoria". E sì, puoi chiamarlo con un local, basta rimuovere la stringa dall'ambiente (putenv("FOO=") o unsetenv) prima di tornare dalla funzione.

Il punto è che l'uso di putenv rende il processo di rimozione di una stringa dall'ambiente completamente deterministico. Considerando che setenv su alcune implementazioni esistenti modificherà una stringa esistente nell'ambiente se il nuovo valore è più breve (per evitare sempre perdita di memoria) e poiché ha creato una copia quando hai chiamato setenv, non hai il controllo della stringa originariamente allocata dinamicamente, quindi non puoi liberarla quando viene rimossa.

Nel frattempo, setenv lo stesso (o unsetenv) non possono liberare la stringa precedente, poiché - anche ignorando putenv - la stringa potrebbe provenire dall'ambiente originale invece di essere allocata da una precedente invocazione di setenv.

(L'intera risposta presuppone un putenv correttamente implementato, cioè not quello in glibc 2.0-2.1.1 che hai citato.)


Linux
  1. Le 30 migliori domande e risposte per le interviste OpenStack

  2. Le 25 migliori domande e risposte per le interviste su Linux

  3. 20 Domande e risposte sull'intervista Postfix

  4. BIND – Domande e risposte sull'intervista al server DNS

  5. 10 fatti interessanti e divertenti su Linux

20 Domande e risposte sull'intervista a Red Hat Satellite Server

Come impostare ed elencare le variabili di ambiente in Linux

10 curiosità su Linus Torvalds e Linux

Come impostare e annullare l'impostazione delle variabili di ambiente su Linux

Miglior ambiente desktop Linux:15 recensiti e confrontati

Le migliori combinazioni di distribuzione Linux e ambiente desktop