プロセスの環境変数を読み取る方法


回答:


20

/proc/$pid/environプロセスが独自の環境を変更した場合に更新します。しかし、多くのプログラムは少し無意味であるため、独自の環境を変更することはありません:プログラムの環境は通常のチャネルでは見えず、とのみで/procありps、すべてのUNIXバリアントがこのような機能を持っているわけではないため、アプリケーションは依存しませんその上。

カーネルに関する限り、環境execveはプログラムを起動するシステムコールの引数としてのみ表示されます。Linuxはを通じてメモリ内の領域を公開し、/proc一部のプログラムはこの領域を更新しますが、他のプログラムは更新しません。特に、この領域を更新するシェルはないと思います。エリアのサイズは固定されているため、新しい変数を追加したり、値の長さを変更したりすることはできません。


そのため、実質的にプロセスの* envp(環境設定へのポインターの配列)にアクセスする方法はありません。@Gilles、デバッガーをアタッチして環境設定へのポインターの配列を読み取ることが可能かどうかを示してください。
ニキルマレー

2
@Nikhil確かにそうです。しかし、あなたPATH=fooがシェルで書いたからといって、シェルが変更しようとしているわけではありません*envp。一部のシェルでは、内部データ構造のみが更新され、更新されるのは外部プログラム実行コードです*envp。見てくださいassign_in_envvariables.c、たとえば、bashのソースで。
ジル 'SO-邪悪な停止

9
@Gilles:この答えはせいぜい誤解を招くだけです(-1)。/ proc / $$ / environの環境は、プロセスのスタックから読み取られます。fs / proc / base.cを参照してください。これが初期環境です。更新されることはなく、実際には更新できません。libc setenvが使用する環境はヒープに割り当てられ、スタック内の環境の内容で初期化されます。プロセスがlibcを呼び出す場合、forklibcはsys_fork、子プロセスに割り当てられたヒープ環境を使用して呼び出しを行います。
ジョナサンベンアブラハム

7
@ JonathanBen-Avraham確かに、初期環境はどのシェルでも更新されません。ただし、その領域はLinuxでのみ読み取られるわけではなく、それを使用してステータスを報告するプログラムに遭遇しました(ステータスレポートargvはより一般的ですが、両方とも存在します)。
ジル 'SO-悪であるのをやめる'

39

プロセスの初期環境はから読むことができます/proc/<pid>/environ

プロセスその環境を変更する場合、環境を読み取るには、プロセスのシンボルテーブルが必要であり、ptraceシステムコールを使用して(たとえば、を使用してgdb)グローバルchar **__environ変数から環境を読み取る必要があります。実行中のLinuxプロセスから変数の値を取得する他の方法はありません。

それが答えです。次に、いくつかの注意事項について説明します。

上記は、プロセスがPOSIX準拠であると想定しています。つまり、プロセスはRef Specでchar **__environ指定されたグローバル変数を使用して環境を管理します。

プロセスの初期環境は、プロセスのスタック上の固定長バッファーでプロセスに渡されます。(これを行う通常のメカニズムはlinux//fs/exec.c:do_execve_common(...)。)バッファーのサイズは初期環境に必要なサイズ以下になるように計算されるため、既存の変数を消去したりスタックを破壊したりせずに新しい変数を追加することはできません。したがって、プロセスの環境の変更を許可する合理的なスキームはヒープを使用します。ヒープでは、任意のサイズのメモリを割り当てたり、解放したりできます。これはまさにGNU libcglibc)が行うことです。

プロセスがを使用する場合、glibcPOSIX準拠__environですglibc//posix/environ.c。Glibc で宣言されると、プロセスのヒープからの__environメモリへのポインターで初期化さmallocれ、スタックからこのヒープ領域に初期環境がコピーされます。プロセスが使用する各時間setenv関数は、glibcないreallocこと領域の大きさを調整する__environポイントに新しい値または変数を収容するために。(glibcのソースコードはでダウンロードできますgit clone git://sourceware.org/git/glibc.git glibc)。メカニズムを本当に理解するには、Hurdコードhurd//init/init.c:frob_kernel_process()(git clone git://git.sv.gnu.org/hurd/hurd.git hurd)を読む必要もあります。

新しいプロセスが専用の場合は今fork編、後続がなくて、execスタックを上書きし、その後、引数と環境のコピー魔法がで行われているlinux//kernel/fork.c:do_fork(...)場合は、copy_processルーチン呼び出しdup_task_struct呼び出して新しいプロセスのスタックを割り当てalloc_thread_info_node、呼び出しsetup_thread_stacklinux//include/linux/sched.h新しいプロセス用)を使用しますalloc_thread_info_node

最後に、POSIX __environ規則はユーザー空間の規則です。Linuxカーネルには何も接続されていません。グローバルを使用glibcせずに__environグローバル変数を使用せずにユーザー空間プログラムを作成し、環境変数を自由に管理できます。これを行うことで誰もあなたを逮捕することはできませんが、独自の環境管理機能(setenv/ getenv)と独自のラッパーを作成sys_execする必要があり、環境に変更を加えた場所を誰も推測できないでしょう。


のファイルの多くは、/proc/[pid]/奇妙なエンコーディングを持っているようです(他の誰かが何を、なぜ知っているかもしれません)。私にとってはcat environ、環境変数を非常に読みにくい形式で出力するだけです。 cat environ | strings私のためにこれを解決しました。
レトロハッカー

:これは、より堅牢なソリューション提供します@retrohacker askubuntu.com/questions/978711/...
フランクKusters

20

プロセスが環境変数を取得/削除するときに更新されます。environ/ procファイルシステムの下のプロセスディレクトリで、プロセスのファイルが更新されていないことを示すリファレンスがありますか?

xargs --null --max-args=1 echo < /proc/self/environ

または

xargs --null --max-args=1 echo < /proc/<pid>/environ

または

ps e -p <pid>

上記は、プロセスの環境変数をps出力形式で出力します。環境変数をリストとして表示するには、テキスト処理(解析/フィルタリング)が必要です。

Solaris(質問はありませんが、参考のためにここに投稿します):

/usr/ucb/ps -wwwe <pid>

または

pargs -e <pid> 

編集: / proc / pid / environは更新されません!私は立ち直る。検証プロセスは次のとおりです。ただし、プロセスの分岐元の子はプロセス環境変数を継承し、それぞれの/ proc / self / environファイルに表示されます。(文字列を使用)

with the shell:ここで、xargsは子プロセスであるため、環境変数を継承し、その/proc/self/environファイルにも反映されます。

[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ export MASK=NIKHIL
[centos@centos t]$ printenv  | grep MASK
MASK=NIKHIL
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
MASK=NIKHIL
[centos@centos t]$ unset MASK
[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
[centos@centos t]$

ターミナル/セッションが環境変数が設定されているシェルの子プロセスではない他のセッションからチェックします。

同じホスト上の別の端末/セッションから確認する:

terminal1:: printenvはフォークされ、bashの子プロセスであるため、独自のenvironファイルを読み取ることに注意してください。

[centos@centos t]$ echo $$
2610
[centos@centos t]$ export SPIDEY=NIKHIL
[centos@centos t]$ printenv | grep SPIDEY
SPIDEY=NIKHIL
[centos@centos t]$ 

terminal2:同じホストで-上記の変数が設定された同じシェルで起動しないで、ターミナルを個別に起動します。

[centos@centos ~]$ echo $$
4436
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/self/environ | grep -i spidey
[centos@centos ~]$ strings -f /proc/2610/environ | grep -i spidey
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/2610/environ | grep -i spidey
[centos@centos ~]$ 

1
私はexport foo=bar1つのbashののセッション(PID XXXX)で、その後、行うcat /proc/xxxx/environ | tr \\0 \\n他のbashのセッションでは、私は表示されませんfoo

上記の回答を、シェル内で同じプロセスをチェックする例で更新しました。
ニキルマレー

あなたは正しいです。私は立ち直る。ありがとう。ユーザープロセスグループ内の別のプロセスの環境変数を確認するために、マニュアルに目を通す必要があります。
ニキルマレー

1
もう1つ:gdb pidをアタッチする環境をチェックしようとしましたが、まだ参照がありません。メモリ内の環境変数ブロックは、変更があるたびに再割り当てされ、procファイルシステム内の独自のプロセスの環境ファイルに反映されませんが、子プロセスによる継承は許可されます。つまり、これにより、フォークが発生したときに固有の詳細を簡単に知ることができ、子プロセスが環境変数をそのままコピーする方法がわかります。
ニキルマレー

私は.. @Gillesはこれに彼のトーチライトの一部を投げるでしょう願っています:-)
ニキルMulley

7

まあ、次は著者の本当の意図とは関係ありませんが、あなたが本当に「読む」こと/proc/<pid>/environを望むなら、あなたは試みるかもしれません

strings /proc/<pid>/environ

それはcatそれよりも優れています。


1
+1 strings。複雑にしないでおく。
エド・ランドール

@EdRandallに同意してください。これは、より簡単なアプローチと思われxargs --nullます。
ランドバーグ

「ファイル」、例えば、(通常の警告で)再び動作改行と通常のツールでNULLを置き換える、NULL終端である:tr '\0' '\n' < /proc/$$/environ | ...
トール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.