静的初期化子で、つまりmain()の前にgetenv()を使用しても安全ですか?
-
22-07-2019 - |
質問
Stevens 、および Posixプログラマーズガイド、私が見つけられる最高のものは
プロセスが開始されると、 enviroment と呼ばれる文字列の配列が使用可能になります。 この配列は、次のように定義されている外部変数
environ
によってポイントされます。
extern char ** environ;
その environ 変数が迷っています。言いたい
-呼び出し元のプロセス/シェルは、すでにnullで終了した文字列のブロックを割り当てています
-「外部」変数 environ
は、 getenv()によってエントリポイントとして使用されます。
- ipso facto は、静的イニシャライザ内で getenv()を自由に呼び出します。
しかし、 environ の「静的初期化」が他のすべての静的初期化コードに先行するという保証はありません。私はこれを考えていますか?
更新
my プラットフォーム(AMD Opteron、Redhat 4、GCC 3.2.3)で LD_DEBUG を設定すると、 environ が設定される before 静的イニシャライザが呼び出されます。これは知っておくといいことです。ありがとう、@ codelogic。しかし、必ずしもすべてのプラットフォームで得られる結果ではありません。
また、C / C ++ランタイムライブラリの動作について@ChrisWに直感的に同意しますが、これは経験に基づく私の直感にすぎません。そのため、静的イニシャライザが呼び出される前に environ が存在することを保証する、信頼できる場所からの引用をパイプで渡すことができる人なら誰でも、ボーナスポイントです!
解決
LD_DEBUGを設定してプログラムを実行し、正確な順序を確認できると思います:
LD_DEBUG=all <myprogram>
編集: 実行時リンカー(glibc 2.7)のソースコード、特にファイルを見ると:
- sysdeps / unix / sysv / linux / init-first.c
- sysdeps / i386 / init-first.c
- csu / libc-start.c
- sysdeps / i386 / elf / start.S
グローバルコンストラクターが呼び出される前に、argc、argv、environ( __
environのエイリアス)が設定されていることがわかります(init関数)。実際のエントリポイント(start.S)である_startから実行を開始できます。スティーブンスを引用したように、&quot;プロセスが開始されると、環境と呼ばれる文字列の配列が使用可能になります&quot; 。プロセスの初期化の最初に環境の割り当てが行われることを示唆しています。これは、同じことをするリンカコードによって裏付けられているので、十分な安心感が得られるはずです:-)
編集2:また、言及する価値があるのは、実行時リンカーでさえクエリを実行して冗長出力(LD_DEBUG)を行うかどうかを判断できるように、環境が早期に設定されることです。
他のヒント
環境のセットアップと静的初期化子の呼び出しの両方が、言語ランタイムが main()が呼び出される前に実行する必要がある関数であることを考えると、保証があるかどうかわかりませんここに。つまり、この が機能するという特定の要件、およびANSI言語やライブラリの仕様などでmain()の前に順序が保証されるという特定の要件を認識していません... 。しかし、私はどちらかを確認するためにチェックしませんでした。
同時に、どの初期化ライブラリ関数を静的初期化子から呼び出すことができるかを制限する特定の要件を知りません。そして、さらに重要なことに、環境からアクセスできない場合、実行時のバグのように感じます(私にとって)。
これに基づいて、私はこれがうまくいくと期待していると投票し、安全な仮定であり、現在のデータポイントはこの推論のラインをサポートしているようです。
私の経験では、Cランタイムライブラリは、ランタイムが静的変数の初期化子を呼び出す前に初期化されます(したがって、初期化子がCランタイムライブラリ関数を呼び出す場合があります)。