GNU/Linux >> Linux の 問題 >  >> Linux

putenv() と setenv() に関する質問

  • [The] putenv(char *string); [...] コールには致命的な欠陥があるようです。

はい、致命的な欠陥です。 POSIX (1988) に保存されたのは、それが先行技術だったからです。 setenv() メカニズムは後で到着しました。 訂正: POSIX 1990 標準では、§B.4.6.1 で「追加関数 putenv() およびclearenv() 1997 年の Single Unix Specification (SUS) バージョン 2 には、putenv() がリストされています。 setenv() ではありません または unsetenv() .次の改訂 (2004 年) では、両方の setenv() が定義されていました そして unsetenv()

<ブロック引用>

渡された文字列をコピーしないため、ローカルで呼び出すことはできず、ヒープに割り当てられた文字列が上書きされたり誤って削除されたりしないという保証はありません。

ローカル変数が putenv() に渡すのはほぼ常に悪い選択であることは正しいです —例外はほとんど存在しないという点まであいまいです。文字列がヒープに割り当てられている場合 (malloc() を使用) など)、コードがそれを変更しないようにする必要があります。もしそうなら、それは同時に環境を変更しています.

<ブロック引用>

さらに (私はテストしていませんが)、環境変数の 1 つの用途は子の環境に値を渡すことであるため、子が exec*() のいずれかを呼び出した場合、これは役に立たないようです。 機能。それは間違っていますか?

exec*() 関数は環境のコピーを作成し、それを実行されたプロセスに渡します。問題ありません。

<ブロック引用>

Linux のマニュアル ページは、glibc 2.0-2.1.1 が上記の動作を放棄し、文字列のコピーを開始したことを示していますが、これによりメモリ リークが発生し、glibc 2.1.2 で修正されました。このメモリ リークが何であるか、またはどのように修正されたかは、私にははっきりしません。

putenv() を呼び出すと、メモリ リークが発生します。 文字列を使用すると、その文字列がまだ使用されているかどうかがわからないため、その文字列を再度使用することはできませんが、値を上書きして変更することはできます (名前を環境変数の名前に変更すると、結果が不確定になります)環境内の別の場所で見つかります)。したがって、スペースを割り当てた場合、従来の putenv() 変数を再度変更するとリークします。 putenv()のとき putenv() のため、割り当てられた変数が参照されなくなりました。 引数への参照は保持されなくなりましたが、ユーザーは環境がそれを参照していると予想したため、メモリ リークが発生しました。何が修正されたのかわかりません — 3/4 は古い動作に戻ることだと思います.

<ブロック引用>

setenv() 文字列をコピーしますが、それがどのように機能するか正確にはわかりません。プロセスのロード時に環境用のスペースが割り当てられますが、固定されています。

元の環境空間は固定されています。変更を開始すると、ルールが変更されます。 putenv() でも 、元の環境が変更され、新しい変数を追加した結果、または既存の変数をより長い値を持つように変更した結果として大きくなる可能性があります。

<ブロック引用>

ここで何らかの (恣意的な?) 規則が働いていますか?たとえば、現在使用されているよりも多くのスロットを env 文字列ポインター配列に割り当て、必要に応じてヌル終了ポインターを下に移動しますか?

それが setenv() です 仕組みができそうです。 (グローバル) 変数 environ 環境変数へのポインターの配列の先頭を指します。ある時点でメモリの 1 つのブロックを指し、別の時点で別のブロックを指している場合、環境はそのように切り替えられます。

<ブロック引用>

新しい (コピーされた) 文字列のメモリは、環境自体のアドレス空間に割り当てられていますか? 大きすぎて収まらない場合は、ENOMEM を取得するだけですか?

はい、ENOMEM を取得できますが、かなりの努力が必要です。また、環境が大きくなりすぎると、他のプログラムを適切に実行できなくなる可能性があります。環境が切り捨てられるか、実行操作が失敗します。

<ブロック引用>

上記の問題を考慮して、setenv() よりも putenv() を好む理由はありますか?

  • setenv() を使用 新しいコードで。
  • 古いコードを更新して setenv() を使用する 、しかしそれを最優先事項にしないでください。
  • putenv() は使用しないでください 新しいコードで。

特別な「環境」スペースはありません - setenv は文字列にスペースを動的に割り当てるだけです (malloc を使用) 例)通常どおりに。環境には、各文字列がどこから来たのかを示す情報が含まれていないため、setenv は不可能です。 または unsetenv setenv への以前の呼び出しによって動的に割り当てられた可能性のあるスペースを解放します。

「渡された文字列をコピーしないため、ローカルで呼び出すことはできず、ヒープに割り当てられた文字列が上書きされたり、誤って削除されたりしないという保証はありません。」 putenv の目的は、ヒープに割り当てられた文字列がある場合に、意図的に削除できるようにすることです。 .これが、「メモリ リークを許可せずに環境に追加できる唯一の関数」という理論的根拠のテキストが意味することです。はい、ローカルで呼び出すことができます。環境から文字列を削除するだけです (putenv("FOO=") または unsetenv) 関数から戻る前に。

要点は、putenv を使用すると、環境から文字列を削除するプロセスが完全に決定論的になるということです。 setenv は、一部の既存の実装では、新しい値が短い場合に環境内の既存の文字列を変更します (常にを避けるため)。 setenv を呼び出したときにコピーを作成したため、最初に動的に割り当てられた文字列を制御できないため、削除されたときに解放できません。

一方、setenv 自体 (または unsetenv) は前の文字列を解放できません。これは、putenv を無視しても、文字列が以前の setenv の呼び出しによって割り当てられたのではなく、元の環境から取得された可能性があるためです。

(この全体の回答は、正しく実装された putenv、つまり not を前提としています あなたが言及した glibc 2.0-2.1.1 のものです。)


Linux
  1. Linuxに関する10の面白くて楽しい事実

  2. ターミナルとシェル環境の色付け?

  3. MemとVmemについて?

  1. OpenStackインタビューの質問と回答のトップ30

  2. Linuxインタビューの質問と回答のトップ25

  3. 保存されたユーザー ID に関する質問

  1. 20ポストフィックスインタビューの質問と回答

  2. BIND –DNSサーバーインタビューの質問と回答

  3. Linux環境変数のヒントとコツ