Linuxでpmapコマンドを使用する方法

公開: 2022-06-25
bashプロンプトを表示するLinuxラップトップ
fatmawati achmad zaenuri / Shutterstock.com

Linuxプロセスが使用するRAMの量を調べることは簡単なことではありません。特に、共有メモリを考慮する必要がある場合はそうです。 ありがたいことに、 pmapコマンドはすべてを理解するのに役立ちます。

メモリマッピング

最新のオペレーティングシステムでは、各プロセスは、メモリまたは割り当てスペースの独自に割り当てられた領域に存在します。 割り当てられた領域の境界は、物理ハードウェアアドレスに直接マップされません。 オペレーティングシステムは、プロセスごとに仮想メモリスペースを作成し、仮想メモリを物理メモリにマッピングする抽象化レイヤーとして機能します。

カーネルは各プロセスの変換テーブルを維持し、これはCPUによってアクセスされます。 カーネルが特定のCPUコアで実行されているプロセスを変更すると、プロセスとCPUコアを結び付ける変換テーブルが更新されます。

抽象化の利点

このスキームには利点があります。 メモリの使用は、ユーザーランドのプロセスごとにある程度カプセル化およびサンドボックス化されています。 プロセスは、仮想メモリアドレスに関してのみメモリを「認識」します。 これは、オペレーティングシステムによって提供されたメモリでのみ機能することを意味します。 共有メモリにアクセスできない限り、他のプロセスに割り当てられたメモリについては知らず、アクセスもできません。

LinuxでのSwappinessとは何ですか? (およびそれを変更する方法)
関連LinuxのSwappinessとは何ですか? (およびそれを変更する方法)

ハードウェアベースの物理メモリを仮想メモリアドレスに抽象化すると、カーネルは、一部の仮想メモリがマップされている物理アドレスを変更できます。 仮想メモリの領域が指す実際のアドレスを変更することにより、メモリをディスクにスワップできます。 また、実際に必要になるまで、物理メモリの提供を延期することもできます。

メモリの読み取りまたは書き込みの要求が要求されたとおりに処理される限り、カーネルは適切と思われるマッピングテーブルを自由に操作できます。

RAMオンデマンド

マッピングテーブルと「RAMオンデマンド」の概念により、共有メモリの可能性が広がります。 カーネルは、同じものをメモリに複数回ロードしないようにします。 たとえば、共有ライブラリを一度メモリにロードし、それを使用する必要のあるさまざまなプロセスにマップします。 各プロセスには、共有ライブラリ用の独自の一意のアドレスがありますが、それらはすべて同じ実際の場所を指します。

メモリの共有領域が書き込み可能である場合、カーネルはコピーオンライトと呼ばれるスキームを使用します。 1つのプロセスが共有メモリに書き込み、そのメモリを共有する他のプロセスが変更を認識しない場合、書き込み要求の時点で共有メモリのコピーが作成されます。

初心者オタク:仮想マシンを作成して使用する方法
関連する初心者オタク:仮想マシンを作成して使用する方法

2009年12月にリリースされたLinuxカーネル2.6.32は、Linuxに「KernelSamePageMerging」と呼ばれる機能を提供しました。 これは、Linuxが異なるアドレス空間にあるデータの同一の領域を検出できることを意味します。 1台のコンピューターで実行されている一連の仮想マシンを想像してみてください。仮想マシンはすべて同じオペレーティングシステムを実行しています。 共有メモリモデルとコピーオンライトを使用すると、ホストコンピューターのオーバーヘッドを大幅に削減できます。

これらすべてにより、Linuxでのメモリ処理が洗練され、可能な限り最適化されます。 しかし、その高度化により、プロセスを調べて、そのメモリ使用量が実際に何であるかを知ることが困難になります。

pmapユーティリティ

カーネルは、「/proc」システム情報疑似ファイルシステム内の2つの疑似ファイルを介してRAMで実行していることの多くを公開します。 プロセスごとに2つのファイルがあり、各プロセスのプロセスIDまたはPIDにちなんで名付けられています。「/ proc/maps」と「/proc//smaps」です。

pmapツールはこれらのファイルから情報を読み取り、結果をターミナルウィンドウに表示します。 pmapを使用するときはいつでも、関心のあるプロセスのPIDを提供する必要があることは明らかです。

プロセスIDの検索

プロセスのPIDを見つける方法はいくつかあります。 これが、例で使用する簡単なプログラムのソースコードです。 Cで書かれています。ターミナルウィンドウにメッセージを出力し、ユーザーが「Enter」キーを押すのを待つだけです。

 #include <stdio.h>

int main(int argc、char * argv [])
{{
  printf( "ハウツーオタクテストプログラム。");
  getc(stdin);
}//メインの終わり

プログラムは、 gccコンパイラを使用してpmという実行可能ファイルにコンパイルされました。

 gcc -o pm pm.c 

サンプルプログラムのコンパイル

プログラムはユーザーが「Enter」を押すのを待つため、好きなだけ実行を続けます。

 ./pm 

サンプルプログラムの実行

プログラムが起動し、メッセージを出力して、キーストロークを待ちます。 これで、そのPIDを検索できます。 psコマンドは、実行中のプロセスを一覧表示します。 -e (すべてのプロセスを表示)オプションを使用すると、 psにすべてのプロセスが一覧表示されます。 出力をgrepにパイプ処理し、名前に「pm」が含まれるエントリを除外します。

 ps -e | grep pm 

grepでプロセスIDを見つける

これにより、名前のどこかに「pm」が含まれるすべてのエントリが一覧表示されます。

pidofコマンドを使用してより具体的にすることができます。 コマンドラインで目的のプロセスの名前をpidofに指定し、一致するものを見つけようとします。 一致するものが見つかった場合、 pidofは一致するプロセスのPIDを出力します。

 pidof pm 

pidofを使用してプロセスIDを見つける

プロセス名がわかっている場合はpidofメソッドの方が便利ですが、プロセス名の一部しかわからない場合でもpsメソッドは機能します。

pmapの使用

テストプログラムを実行し、そのPIDを特定したら、次のようにpmapを使用できます。

 pmap 40919 

サンプルプログラムでpmapを実行する

プロセスのメモリマッピングが一覧表示されます。

標準のpmap出力

コマンドからの完全な出力は次のとおりです。

 40919:./pm
000056059f06c000 4K r ---- pm
000056059f06d000 4K rx-- pm
000056059f06e000 4K r ---- pm
000056059f06f000 4K r ---- pm
000056059f070000 4K rw --- pm
000056059fc39000 132K rw --- [anon]
00007f97a3edb000 8K rw --- [anon]
00007f97a3edd000 160K r ---- libc.so.6
00007f97a3f05000 1616K rx-- libc.so.6
00007f97a4099000 352K r ---- libc.so.6
00007f97a40f1000 4K ----- libc.so.6
00007f97a40f2000 16K r ---- libc.so.6
00007f97a40f6000 8K rw --- libc.so.6
00007f97a40f8000 60K rw --- [anon]
00007f97a4116000 4K r ---- ld-linux-x86-64.so.2
00007f97a4117000 160K rx-- ld-linux-x86-64.so.2
00007f97a413f000 40K r ---- ld-linux-x86-64.so.2
00007f97a4149000 8K r ---- ld-linux-x86-64.so.2
00007f97a414b000 8K rw --- ld-linux-x86-64.so.2
00007ffca0e7e000 132K rw---[スタック]
00007ffca0fe1000 16K r ---- [anon]
00007ffca0fe5000 8K rx-- [anon]
ffffffffff600000 4K --x-- [anon]
合計2756K

最初の行はプロセス名とそのPIDです。 他の各行には、マップされたメモリアドレスと、そのアドレスのメモリ量がキロバイト単位で表示されます。 各行の次の5文字は、仮想メモリのアクセス許可と呼ばれます。 有効な権限は次のとおりです。

  • r :マップされたメモリはプロセスによって読み取ることができます。
  • w :マップされたメモリはプロセスによって書き込むことができます。
  • x :プロセスは、マップされたメモリに含まれる任意の命令を実行できます。
  • :マップされたメモリは共有され、共有メモリに加えられた変更は、メモリを共有するすべてのプロセスに表示されます。
  • R :このマップされたメモリのスワップスペースの予約はありません。

各行の最終情報は、マッピングのソースの名前です。 これは、プロセス名、ライブラリ名、またはスタックやヒープなどのシステム名にすることができます。

拡張ディスプレイ

-x (拡張)オプションは、2つの追加の列を提供します。

 pmap -x 40919 

pmapで-X拡張オプションを使用する

列にはタイトルが付けられています。 「アドレス」、「キロバイト」、「モード」、および「マッピング」の列はすでに見てきました。 新しい列は「RSS」および「ダーティ」と呼ばれます。

pmap拡張出力

完全な出力は次のとおりです。

 40919:./pm
アドレスキロバイトRSSダーティモードマッピング
000056059f06c000 4 4 0 r ---- pm
000056059f06d000 4 4 0 rx-- pm
000056059f06e000 4 4 0 r ---- pm
000056059f06f000 4 4 4 r ---- pm
000056059f070000 4 4 4 rw --- pm
000056059fc39000 132 4 4 rw --- [anon]
00007f97a3edb000 8 4 4 rw --- [anon]
00007f97a3edd000 160160 0 r ---- libc.so.6
00007f97a3f05000 1616 788 0 rx-- libc.so.6
00007f97a4099000 352 64 0 r ---- libc.so.6
00007f97a40f1000 4 0 0 ----- libc.so.6
00007f97a40f2000 16 16 16 r ---- libc.so.6
00007f97a40f6000 8 8 8 rw --- libc.so.6
00007f97a40f8000 60 28 28 rw --- [anon]
00007f97a4116000 4 4 0 r ---- ld-linux-x86-64.so.2
00007f97a4117000 160160 0 rx-- ld-linux-x86-64.so.2
00007f97a413f000 40 40 0 r ---- ld-linux-x86-64.so.2
00007f97a4149000 8 8 8 r ---- ld-linux-x86-64.so.2
00007f97a414b000 8 8 8 rw --- ld-linux-x86-64.so.2
00007ffca0e7e000 132 12 12 rw---[スタック]
00007ffca0fe1000 16 0 0 r ---- [anon]
00007ffca0fe5000 8 4 0 rx-- [anon]
ffffffffff600000 4 0 0 --x-- [anon]
---------------- ------- ------- ------- 
合計kB27561328 96
  • RSS :これは常駐セットのサイズです。 つまり、現在RAMにあり、スワップアウトされていないメモリの量です。
  • ダーティ:「ダーティ」メモリは、プロセス(およびマッピング)が開始されてから変更されています。

すべて見せて

-X (拡張以上)は、出力に列を追加します。 大文字の「X」に注意してください。 -XX-Xよりも大きい)と呼ばれる別のオプションは、 pmapがカーネルから取得できるすべてのものを示します。 -X-XXのサブセットであるため、 -XXからの出力について説明します。

 pmap -XX 40919 

-XXを使用すると、pmapですべてを表示するオプションが表示されます

出力はターミナルウィンドウでひどく回り込み、実際には判読できません。 完全な出力は次のとおりです。

 40919:./pm
         アドレスパーマオフセットデバイスiノードサイズKernelPageSizeMMUPageSizeRss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked THPeligible VmFlags Mapping
    56059f06c000 r--p 00000000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd pm
    56059f06d000 r-xp 00001000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me dw sd pm
    56059f06e000 r--p 00002000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd pm
    56059f06f000 r--p 00002000 08:03 393304 4 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw ac sd pm
    56059f070000 rw-p 00003000 08:03 393304 4 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me dw ac sd pm
    56059fc39000 rw-p 00000000 00:00 0132 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me acsd[ヒープ]
    7f97a3edb000 rw-p 00000000 00:00 0 8 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd 
    7f97a3edd000 r--p 00000000 08:03 264328 160 4 4160 4160 0 0 0160 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me sd libc.so.6
    7f97a3f05000 r-xp 00028000 08:03 264328 1616 4 4788 32788 0 0 0 788 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me sd libc.so.6
    7f97a4099000 r--p 001bc000 08:03 264328 352 4 4 64 1 64 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me sd libc.so.6
    7f97a40f1000 --- p 00214000 08:03 264328 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 mr mw me sd libc.so.6
    7f97a40f2000 r--p 00214000 08:03 264328 16 4 4 16 16 0 0 0 16 16 16 0 0 0 0 0 0 0 0 0 0 rd mr mw me ac sd libc.so.6
    7f97a40f6000 rw-p 00218000 08:03 264328 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd libc.so.6
    7f97a40f8000 rw-p 00000000 00:00 0 60 4 4 28 28 0 0 0 28 28 28 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd 
    7f97a4116000 r--p 00000000 08:03 264305 4 4 4 4 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd ld-linux-x86-64.so.2
    7f97a4117000 r-xp 00001000 08:03 264305 160 4 4160 11160 0 0 0160 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me dw sd ld-linux-x86-64.so.2
    7f97a413f000 r--p 00029000 08:03 264305 40 4 4 40 1 40 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd ld-linux-x86-64.so.2
    7f97a4149000 r--p 00032000 08:03 264305 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw ac sd ld-linux-x86-64.so.2
    7f97a414b000 rw-p 00034000 08:03 264305 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me dw ac sd ld-linux-x86-64.so.2
    7ffca0e7e000 rw-p 00000000 00:00 0132 4 4 12 12 0 0 0 12 12 12 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me gdac[スタック]
    7ffca0fe1000 r--p 00000000 00:00 0 16 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 rd mr pf io de dd sd [vvar]
    7ffca0fe5000 r-xp 00000000 00:00 0 8 4 4 4 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me de sd [vdso]
ffffffffff600000 --xp 00000000 00:00 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ex [vsyscall]
                                             ==== ============== =========== ==== === ============ == ========== ============= ============= ========== ==== ===== ======== ============= ============== ========== === ============== =============== ==== ======= ====== = ========== 
                                             2756 92 92 1328 157 1220 0 12 96 1328 96 0 0 0 0 0 0 0 0 0 0 KB 

ここにはたくさんの情報があります。 これは列が保持するものです:

  • アドレス:このマッピングの開始アドレス。 これは仮想メモリアドレス指定を使用します。
  • Perm :メモリの権限。
  • オフセット:メモリがファイルベースの場合、ファイル内のこのマッピングのオフセット。
  • デバイス:Linuxデバイス番号。メジャー番号とマイナー番号で示されます。 lsblkコマンドを実行すると、コンピューター上のデバイス番号を確認できます。
  • iノード:マッピングが関連付けられているファイルのiノード。 たとえば、この例では、これはpmプログラムに関する情報を保持するiノードである可能性があります。
  • サイズ:メモリマップ領域のサイズ。
  • KernelPageSize :カーネルが使用するページサイズ。
  • MMUPageSize :メモリ管理ユニットが使用するページサイズ。
  • Rss :これは常駐セットのサイズです。 つまり、現在RAMにあり、スワップアウトされていないメモリの量です。
  • Pss :これは比例シェアサイズです。 これは、(共有サイズを共有数で割った値)に追加されたプライベート共有サイズです。
  • Shared_Clean :マッピングが作成されてから変更されていない他のプロセスと共有されているメモリの量。 メモリが共有可能であっても、実際に共有されていない場合は、プライベートメモリと見なされることに注意してください。
  • Shared_Dirty :マッピングが作成されから変更された他のプロセスと共有されるメモリの量。
  • Private_Clean :マッピングが作成されてから変更されていない(他のプロセスと共有されていない)プライベートメモリの量。
  • Private_Dirty :マッピングが作成されから変更されたプライベートメモリの量。
  • 参照済み:現在参照済みまたはアクセス済みとしてマークされているメモリの量。
  • 匿名:スワップアウトするデバイスがないメモリ。 つまり、ファイルでバックアップされていません。
  • LazyFreeMADV_FREEとしてフラグが立てられたページ。 これらのページには、書き込まれていない変更が含まれている可能性がありますが、解放および再利用できるようにマークされています。 ただし、メモリマッピングにMADV_FREEが設定された後に後続の変更が発生した場合、 MADV_FREEフラグは削除され、変更が書き込まれるまでページは再利用されません。
  • AnonHugePages :これらはファイルでバックアップされていない「巨大な」メモリページ(4 KBより大きい)です。
  • ShmemPmdMapped :巨大なページに関連付けられた共有メモリ。 また、完全にメモリ内にあるファイルシステムでも使用できます。
  • FilePmdMapped :ページミドルディレクトリは、カーネルで使用可能なページングスキームの1つです。 これは、PMDエントリが指すファイルでバックアップされたページの数です。
  • Shared_Hugetlb :トランスレーションルックアサイドテーブル(TLB)は、ユーザースペースのメモリ位置へのアクセスにかかる時間を最適化するために使用されるメモリキャッシュです。 この数値は、共有された巨大なメモリページに関連付けられているTLBで使用されているRAMの量です。
  • Private_Hugetlb :この数値は、プライベートの巨大なメモリページに関連付けられているTLBで使用されているRAMの量です。
  • スワップ:使用されているスワップの量。
  • SwapPssスワップ比例シェアサイズ。 これは、(共有サイズを共有数で割った値)に追加された、スワップされたプライベートメモリページで構成されるスワップの量です。
  • ロック済み:メモリマッピングをロックして、オペレーティングシステムがヒープまたはオフヒープメモリをページアウトするのを防ぐことができます。
  • THPeligible :これは、マッピングが透過的な巨大ページの割り当てに適しているかどうかを示すフラグです。 1は真を意味し、0は偽を意味します。 透過的な巨大ページは、大量のRAMを搭載したコンピューターでのTLBページルックアップのオーバーヘッドを削減するメモリ管理システムです。
  • VmFlags :以下のフラグのリストを参照してください。
  • マッピング:マッピングのソースの名前。 これは、プロセス名、ライブラリ名、またはスタックやヒープなどのシステム名にすることができます。

VmFlags(仮想メモリフラグ)は、次のリストのサブセットになります。

  • rd :読み取り可能。
  • wr :書き込み可能。
  • :実行可能ファイル。
  • sh :共有。
  • mr :読むかもしれません。
  • mw :書くかもしれません。
  • :実行する可能性があります。
  • ms :共有する可能性があります。
  • gd :スタックセグメントが大きくなります。
  • pf :純粋なページフレーム番号の範囲。 ページフレーム番号は、物理メモリページのリストです。
  • dw :マップされたファイルへの書き込みを無効にしました。
  • lo :ページはメモリにロックされています。
  • io :メモリマップドI/Oエリア。
  • sr :( madvise()関数によって提供される)順次読み取りアドバイス。
  • rr :ランダムな読み取りアドバイスが提供されます。
  • dc :プロセスがフォークされている場合は、このメモリ領域をコピーしないでください。
  • de :再マッピング時にこのメモリ領域を拡張しないでください。
  • ac :エリアは責任があります。
  • nr :スワップスペースはエリア用に予約されていません。
  • ht :エリアは巨大なTLBページを使用しています。
  • sf :同期ページフォールト。
  • ar :アーキテクチャ固有のフラグ。
  • wf :プロセスがフォークされている場合は、このメモリ領域をワイプします。
  • dd :このメモリ領域をコアダンプに含めないでください。
  • sd :ソフトダーティフラグ。
  • mm :混合マップエリア。
  • hg :巨大なページアドバイスフラグ。
  • nh :巨大なページアドバイスフラグはありません。
  • mg :マージ可能なアドバイスフラグ。
  • bt :ARM64バイアス温度の不安定性が保護されたページ。
  • mt :ARM64メモリタグ付け拡張タグが有効になっています。
  • um :Userfaultfdに追跡がありません。
  • uw :Userfaultfdwr-protecttracking。

メモリ管理は複雑です

また、データのテーブルから逆方向に作業して、実際に何が起こっているのかを理解するのは困難です。 しかし、少なくともpmapは全体像を示しているので、知っておくべきことを理解する可能性が最も高くなります。

興味深いことに、サンプルプログラムは16 KBのバイナリ実行可能ファイルにコンパイルされていますが、ほぼ完全にランタイムライブラリが原因で、約2756 KBのメモリを使用(または共有)しています。

最後の巧妙なトリックの1つは、プロセスのPIDを見つけて、それをpmapに渡すアクションを1つのコマンドに組み合わせて、 pmapコマンドとpidofコマンドを一緒に使用できることです。

 pmap $(pidof pm)