GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Fragen zu putenv() und setenv()

  • [Der] putenv(char *string); [...] Anruf scheint fatal fehlerhaft.

Ja, es ist fatal fehlerhaft. Es wurde in POSIX (1988) beibehalten, weil dies der Stand der Technik war. Die setenv() Mechanismus ist später angekommen. Korrektur: Der POSIX 1990 Standard sagt in §B.4.6.1 „Zusätzliche Funktionen putenv() und clearenv() berücksichtigt, aber abgelehnt". Die Single Unix Specification (SUS) Version 2 von 1997 listet putenv() auf aber nicht setenv() oder unsetenv() . Die nächste Revision (2004) definierte beide setenv() und unsetenv() auch.

Da es die übergebene Zeichenfolge nicht kopiert, können Sie es nicht lokal aufrufen, und es gibt keine Garantie, dass eine dem Heap zugewiesene Zeichenfolge nicht überschrieben oder versehentlich gelöscht wird.

Sie haben Recht, dass eine lokale Variable fast immer eine schlechte Wahl für die Übergabe an putenv() ist — die Ausnahmen sind so dunkel, dass sie fast nicht mehr existieren. Wird der String auf dem Heap allokiert (mit malloc() et al), müssen Sie sicherstellen, dass Ihr Code ihn nicht ändert. Wenn dies der Fall ist, ändert es gleichzeitig die Umgebung.

Darüber hinaus (obwohl ich es nicht getestet habe) erscheint dies nutzlos, da eine Verwendung von Umgebungsvariablen darin besteht, Werte an die Umgebung des Kindes zu übergeben, wenn das Kind einen der exec*() aufruft Funktionen. Liege ich da falsch?

Die exec*() Funktionen erstellen eine Kopie der Umgebung und übergeben diese an den ausgeführten Prozess. Da gibt es kein Problem.

Die Linux-Manpage gibt an, dass glibc 2.0-2.1.1 das obige Verhalten aufgab und mit dem Kopieren der Zeichenfolge begann, aber dies führte zu einem Speicherleck, das in glibc 2.1.2 behoben wurde. Mir ist nicht klar, was dieses Speicherleck war oder wie es behoben wurde.

Das Speicherleck entsteht, weil Sie einmal putenv() angerufen haben mit einer Zeichenfolge können Sie diese Zeichenfolge für keinen Zweck erneut verwenden, da Sie nicht feststellen können, ob sie noch verwendet wird, obwohl Sie den Wert ändern könnten, indem Sie ihn überschreiben (mit unbestimmten Ergebnissen, wenn Sie den Namen in den einer Umgebungsvariablen ändern an anderer Stelle in der Umgebung gefunden). Also, wenn Sie Speicherplatz zugewiesen haben, der Klassiker putenv() leckt es, wenn Sie die Variable erneut ändern. Wenn putenv() begann, Daten zu kopieren, zugewiesene Variablen wurden unreferenziert, weil putenv() Es wurde kein Verweis mehr auf das Argument beibehalten, aber der Benutzer erwartete, dass die Umgebung darauf verweisen würde, sodass der Speicher verloren ging. Ich bin mir nicht sicher, was der Fix war – ich würde zu 3/4 erwarten, dass das alte Verhalten wiederhergestellt wird.

setenv() kopiert die Zeichenfolge, aber ich weiß nicht genau, wie das funktioniert. Speicherplatz für die Umgebung wird beim Laden des Prozesses zugewiesen, ist aber behoben.

Der ursprüngliche Umgebungsraum ist festgelegt; Wenn Sie anfangen, es zu ändern, ändern sich die Regeln. Auch mit putenv() , wird die ursprüngliche Umgebung geändert und könnte durch das Hinzufügen neuer Variablen oder durch das Ändern vorhandener Variablen mit längeren Werten wachsen.

Ist hier eine (willkürliche?) Konvention am Werk? Zum Beispiel mehr Slots im env-String-Pointer-Array zuweisen als derzeit verwendet werden und den Null-Terminierungszeiger nach Bedarf nach unten verschieben?

Dafür steht der setenv() Mechanismus ist wahrscheinlich zu tun. Die (globale) Variable environ zeigt auf den Anfang des Arrays von Zeigern auf Umgebungsvariablen. Wenn es zu einem Zeitpunkt auf einen Speicherblock und zu einem anderen Zeitpunkt auf einen anderen Block zeigt, dann wird die Umgebung umgeschaltet, einfach so.

Ist der Speicher für den neuen (kopierten) String im Adressraum der Umgebung selbst allokiert und wenn er zu groß ist, holt man sich einfach ENOMEM?

Nun, ja, Sie könnten ENOMEM bekommen, aber Sie müssten sich ziemlich anstrengen. Und wenn Sie die Umgebung zu groß machen, können Sie andere Programme möglicherweise nicht richtig ausführen - entweder wird die Umgebung abgeschnitten oder die Ausführungsoperation schlägt fehl.

Gibt es angesichts der oben genannten Probleme einen Grund, putenv() gegenüber setenv() zu bevorzugen?

  • Verwenden Sie setenv() in neuem Code.
  • Alten Code aktualisieren, um setenv() zu verwenden , aber machen Sie es nicht zur obersten Priorität.
  • Verwenden Sie nicht putenv() in neuem Code.

Es gibt keinen speziellen Platz für „die Umgebung“ – setenv weist einfach dynamisch Platz für die Zeichenfolgen zu (mit malloc zum Beispiel) wie Sie es normalerweise tun würden. Da die Umgebung keinen Hinweis darauf enthält, woher jeder darin enthaltene String stammt, ist dies für setenv nicht möglich oder unsetenv um Speicherplatz freizugeben, der möglicherweise durch vorherige Aufrufe von setenv.

dynamisch zugewiesen wurde

"Da die übergebene Zeichenfolge nicht kopiert wird, können Sie sie nicht lokal aufrufen, und es gibt keine Garantie, dass eine dem Heap zugewiesene Zeichenfolge nicht überschrieben oder versehentlich gelöscht wird." Der Zweck von putenv besteht darin, sicherzustellen, dass es möglich ist, einen Heap-zugewiesenen String absichtlich zu löschen . Das meint der Begründungstext mit „der einzigen verfügbaren Funktion, die der Umgebung hinzugefügt werden kann, ohne Speicherlecks zuzulassen“. Und ja, Sie können es mit einem lokalen aufrufen, entfernen Sie einfach die Zeichenfolge aus der Umgebung (putenv("FOO=") oder unsetenv), bevor Sie von der Funktion zurückkehren.

Der Punkt ist, dass die Verwendung von putenv den Prozess des Entfernens einer Zeichenfolge aus der Umgebung vollständig deterministisch macht. Während setenv bei einigen vorhandenen Implementierungen eine vorhandene Zeichenfolge in der Umgebung ändert, wenn der neue Wert kürzer ist (um zu vermeiden, dass immer Speicherverlust), und da beim Aufruf von setenv eine Kopie erstellt wurde, haben Sie keine Kontrolle über die ursprünglich dynamisch zugewiesene Zeichenfolge, sodass Sie sie nicht freigeben können, wenn sie entfernt wird.

In der Zwischenzeit setenv sich (oder unsetenv) kann den vorherigen String nicht freigeben, da - selbst wenn putenv ignoriert wird - der String möglicherweise aus der ursprünglichen Umgebung stammt, anstatt durch einen vorherigen Aufruf von setenv zugewiesen zu werden.

(Diese ganze Antwort setzt ein korrekt implementiertes putenv voraus, d.h. nicht die in glibc 2.0-2.1.1, die Sie erwähnt haben.)


Linux
  1. Top 30 Fragen und Antworten zu OpenStack-Interviews

  2. Top 25 Fragen und Antworten zu Linux-Interviews

  3. Einfärben Ihrer Terminal- und Shell-Umgebung?

  4. Fragen zu IPTables und DHCP?

  5. Fragen zur gespeicherten Benutzer-ID

20 Red Hat Satellite Server – Fragen und Antworten in Vorstellungsgesprächen

So setzen und listen Sie Umgebungsvariablen in Linux auf

10 lustige Fakten über Linus Torvalds und Linux

So setzen und löschen Sie Umgebungsvariablen unter Linux

Beste Linux-Desktop-Umgebung:15 überprüft und verglichen

Beste Kombinationen aus Linux-Distribution und Desktop-Umgebung