Linuxの/proc/id/mapsファイルを理解する
1. 概要
このチュートリアルでは、 / proc / id/mapsファイルの出力を読み取ることでLinuxプロセスのメモリ使用量をプロファイリングする方法を説明します。 まず、で仮想メモリの概念を説明します。 次に、プロセスの 仮想アドレス空間、その構造、およびその周囲のアクセス許可について説明します。 最後に、 / proc / id / maps の出力を解釈して、特定のプロセスの仮想アドレス空間を表示する方法について説明します。
2. 仮想メモリとは何ですか?
仮想メモリを使用すると、各プロセスは、マシンに存在する使用可能なすべての物理メモリを要求できます。 つまり、各プロセスは、オペレーティングシステムで実行されている唯一のプロセスであるかのように機能します。 このアプローチには多くの利点があります。たとえば、新しいメモリ要求を大幅に簡素化するため、開発者のエクスペリエンスが大幅に向上します。 さらに、プロセスを分離して相互に干渉しないようにするため、セキュリティが向上します。 最後に、エラーが発生した場合、残りのプロセスにオーバーヘッドを追加することなく、単一のプロセスにのみ影響するため、パフォーマンスが向上します。
ただし、仮想メモリの合計量が使用可能な実際のメモリ(スワップスペースを含む)を超える場合は、特別な注意が必要です。 この問題を解決するために、Linuxはメモリ不足キラーを導入しました。
3. プロセスの仮想アドレス空間(VAS)
プロセスの仮想アドレス空間(VAS)は、このプロセスが参照できるすべての使用可能なメモリアドレスで構成されます。 これは仮想メモリであり、マシンにインストールされている物理メモリの合計とほぼ同じです。
VASは2つのリージョンに分割されます。
- カーネル空間仮想アドレスは、ユーザーモードからカーネルモードに切り替わるプロセスによって参照されます。
- ユーザースペースの仮想アドレスには、プロセスのコード、データ、および依存関係を含むさまざまなタイプのセグメントが含まれています
3.1. セグメントタイプ
ユーザーVASセグメントは、マッピングとも呼ばれ、メモリの連続ブロックであり、その内容はセグメントタイプによって異なります。 さまざまなタイプを見てみましょう。
- 実行可能コードを含むコードセグメント(またはテキストセグメント)
- 変数や定数などのプロセスデータを含むデータセグメント。 初期化されたデータセグメント、初期化されていないデータセグメント、、ヒープセグメントにさらに分類されます。
- スタックセグメントは動的に拡張でき、関数パラメーターとローカル関数変数を含めることができます
- プロセスが使用するリンクされた(共有)ライブラリを含む共有ライブラリセグメント
3.2. セグメント権限
これらのセグメントは、それらで許可されるアクションを制御する一連のアクセス許可でマップされます。 これらの権限は、モードと呼ばれることが多く、次のとおりです。
- 読み取り専用(r)は、セグメントが読み取り可能であることを意味します。したがって、通常、すべてのセグメントにそのモードがあります。
- 読み取り/書き込み(w)は、データの変更を可能にするためにセグメントが読み取りおよび書き込み可能であることを意味します
- execute(x)は、セグメントに実行可能コードが含まれていることを意味します
- private(p)は、セグメントがプライベートであるため、そのプロセスからのみ表示されることを意味します
- 共有(s)。これは、複数(少なくとも2)のプロセスがそのセグメントを共有することを意味します
4. / proc / id /mapsを使用したプロセスのVASの調査
プロセスのVASを理解するために、前のセクションで学んだことを適用してみましょう。 プロセスのPIDがわかっているとすると、 procfs を利用して、VASがどのように見えるかを確認できます。 そのためには、 /proc/から読み取ることができます
$ cat /proc/self/maps
559b8c418000-559b8c41a000 r--p 00000000 08:30 1708 /usr/bin/cat
559b8c41a000-559b8c41f000 r-xp 00002000 08:30 1708 /usr/bin/cat
559b8c41f000-559b8c422000 r--p 00007000 08:30 1708 /usr/bin/cat
559b8c422000-559b8c423000 r--p 00009000 08:30 1708 /usr/bin/cat
559b8c423000-559b8c424000 rw-p 0000a000 08:30 1708 /usr/bin/cat
559b8c5d1000-559b8c5f2000 rw-p 00000000 00:00 0 [heap]
7faa72001000-7faa72023000 rw-p 00000000 00:00 0
7faa72023000-7faa72055000 r--p 00000000 08:30 3023 /usr/lib/locale/C.UTF-8/LC_CTYPE
7faa72055000-7faa72056000 r--p 00000000 08:30 3030 /usr/lib/locale/C.UTF-8/LC_NUMERIC
7faa72056000-7faa72057000 r--p 00000000 08:30 3033 /usr/lib/locale/C.UTF-8/LC_TIME
7faa72057000-7faa721ca000 r--p 00000000 08:30 3022 /usr/lib/locale/C.UTF-8/LC_COLLATE
7faa721ca000-7faa721cb000 r--p 00000000 08:30 3028 /usr/lib/locale/C.UTF-8/LC_MONETARY
7faa721cb000-7faa721cc000 r--p 00000000 08:30 3027 /usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES
7faa721cc000-7faa721cd000 r--p 00000000 08:30 3031 /usr/lib/locale/C.UTF-8/LC_PAPER
7faa721cd000-7faa721ce000 r--p 00000000 08:30 3029 /usr/lib/locale/C.UTF-8/LC_NAME
7faa721ce000-7faa721cf000 r--p 00000000 08:30 3021 /usr/lib/locale/C.UTF-8/LC_ADDRESS
7faa721cf000-7faa724b5000 r--p 00000000 08:30 3034 /usr/lib/locale/locale-archive
7faa724b5000-7faa724da000 r--p 00000000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa724da000-7faa72652000 r-xp 00025000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa72652000-7faa7269c000 r--p 0019d000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa7269c000-7faa7269d000 ---p 001e7000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa7269d000-7faa726a0000 r--p 001e7000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa726a0000-7faa726a3000 rw-p 001ea000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa726a3000-7faa726a9000 rw-p 00000000 00:00 0
7faa726a9000-7faa726aa000 r--p 00000000 08:30 3032 /usr/lib/locale/C.UTF-8/LC_TELEPHONE
7faa726aa000-7faa726ab000 r--p 00000000 08:30 3025 /usr/lib/locale/C.UTF-8/LC_MEASUREMENT
7faa726ab000-7faa726b2000 r--s 00000000 08:30 11818 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7faa726b2000-7faa726b3000 r--p 00000000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726b3000-7faa726d6000 r-xp 00001000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726d6000-7faa726de000 r--p 00024000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726de000-7faa726df000 r--p 00000000 08:30 3024 /usr/lib/locale/C.UTF-8/LC_IDENTIFICATION
7faa726df000-7faa726e0000 r--p 0002c000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726e0000-7faa726e1000 rw-p 0002d000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726e1000-7faa726e2000 rw-p 00000000 00:00 0
7ffeb2f53000-7ffeb2f74000 rw-p 00000000 00:00 0 [stack]
7ffeb2f99000-7ffeb2f9d000 r--p 00000000 00:00 0 [vvar]
7ffeb2f9d000-7ffeb2f9e000 r-xp 00000000 00:00 0 [vdso]
4.1. 出力を理解する
上記の出力では、コマンドラインユーティリティcatのユーザーVASを確認できます。 全部で6つの列があることがわかります。 最初の行を見て、各列の内容を説明しましょう。
<address start>-<address end> <mode> <offset> <major id:minor id> <inode id> <file path>
559b8c418000-559b8c41a000 r--p 00000000 08:30 1708 /usr/bin/cat
- address start – address endは、そのマッピングの開始アドレスと終了アドレスです。 全体の出力は、これらのアドレスに基づいて低から高にソートされることに注意してください。
- mode(permissions)は、このマッピングで使用できるアクションと、それがプライベートか共有かを指定します。
- offsetは、マップされたファイル内のバイト単位の開始オフセットです。 このは、ファイルマッピングに対してのみ意味があります。 たとえば、スタックまたはヒープマッピングは、ファイルではないマッピングの例であり、その場合、オフセットは0です。 上記の例では、マッピングは / usr / bin / cat ファイルのものであり、オフセットは0です。
- major:minor idsは、マップされたファイルが存在するデバイスをメジャーIDとマイナーIDの形式で表します。 上記の例では、08:30はルートファイルシステムを持つハードドライブのメジャーIDとマイナーIDを表します。 ファイル以外のマッピングの場合、この列には00:00。が表示されます。
- マップされたファイルのiノードID(これも、ファイルマッピングにのみ有効です)。 iノードは、コアファイルシステム関連のメタデータを含むデータ構造です。 非ファイルマッピングの場合、このフィールドは0に設定されます。 この例では、このIDは1708です。
- そのマッピングのファイルのファイルパス。 これがファイルマッピングでない場合、そのフィールドは空です。
4.2. 行ごとに出力を調べる
これで、出力を1行ずつ調べる準備ができました。 これを短くするために、出力に表示されるすべての異なるタイプのセグメントの代表的な例のみを説明します。 すぐに飛び込みましょう:
まず、 / usr / bin/catファイルの読み取り専用のプライベートマッピングが表示されます。
559b8c418000-559b8c41a000 r--p 00000000 08:30 1708 /usr/bin/cat
これはプライベートデータセグメントであり、おそらくグローバル変数または定数が含まれているため、読み取り専用です。
次に、 / usr / bin / cat:について、読み取り可能で実行可能なマッピングが表示されます。
559b8c41a000-559b8c41f000 r-xp 00002000 08:30 1708 /usr/bin/cat
実行可能であるとすると、このセグメントはコードセグメントです。
第三に、読み取りと書き込みが可能なマッピングが表示されます。
559b8c423000-559b8c424000 rw-p 0000a000 08:30 1708 /usr/bin/cat
繰り返しますが、これはデータセグメントであり、おそらく変数を格納および更新するプロセス用です。
さらに、ヒープセグメントに対応するマッピングを確認できます。
559b8c5d1000-559b8c5f2000 rw-p 00000000 00:00 0 [heap]
これはファイルマッピングではないため、メジャー、マイナー、およびiノードIDは0であることに注意してください。
次に、読み取り専用のプライベートマッピングが表示されます。
7faa72023000-7faa72055000 r--p 00000000 08:30 3023 /usr/lib/locale/C.UTF-8/LC_CTYPE
これは、ロケール関連の定数を含む(読み取り専用)データセグメントです。
最後に、libcの読み取り専用データセグメントとコードセグメントを確認できます。
7faa724b5000-7faa724da000 r--p 00000000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa724da000-7faa72652000 r-xp 00025000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa72652000-7faa7269c000 r--p 0019d000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa7269c000-7faa7269d000 ---p 001e7000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa7269d000-7faa726a0000 r--p 001e7000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7faa726a0000-7faa726a3000 rw-p 001ea000 08:30 11971 /usr/lib/x86_64-linux-gnu/libc-2.31.so
これは、catプロセスで使用される共有ライブラリです。
5. 結論
この記事では、Linuxプロセスのユーザー仮想アドレス空間(VAS)を調べる方法を見ました。 まず、仮想メモリとは何かを簡単に説明しました。 次に、プロセスとそのセグメントのユーザーVASについて説明しました。 次に、 / proc / id / maps ファイルを読んで、VASを深く掘り下げる方法を説明しました。 最後に、catコマンドラインユーティリティのそのファイルからの出力の行ごとの例を見ました。