読者です 読者をやめる 読者になる 読者になる

中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

MongoDB 2.4 Analyzerをアナライズした!

前回のエントリーで釈然としなかったAnalyzerの戻す値が信用出来ない件。
このままじゃ、安心して使えないので解析した。

MongoDB analyzer 問題

db.serverStatus().mem.resident
mongodプロセスが利用している物理メモリサイズ
db.serverStatus({workingSet
1}).workingSet.pageInMemory:mongodがmmapしたデータ(&Journal)ファイルの内、物理メモリに乗ってるページ数(1ページ=4kb)

これらの値はほぼ等しい(かresidentの方が若干大きい)ハズだ。

しかし前回、この値が大きく逆転する現象を見て混乱というか
『こりゃ信用出来ない』
と思った訳だ。

resident
127MB
pageInMemory
448MB

なんで仮想メモリー空間は全部で127MBしか物理メモリーに乗ってないのに
仮想メモリー空間内の領域の一部であるmapファイルはその3倍も物理メモリーに乗ってるのか?(意味不明)

ソースコードを読む

src/mongo/util/processinfo_linux2.cpp に全て書かれていた。

#FF0000;font-size:x-large;">resident: ProcessInfo::getResidentSize()
結局/proc//stat からresidentを得ている事が解った。
#FF0000;font-size:x-large;">pageInMemory: ProcessInfo::pagesInMemory()
mincore()システムコールが出所だった。

しかしちゃんと読んでもバグらしきモノは見つからない・・・

システムコールの先

『昔取ったなんちゃら』カーネルに潜ってくと大体理由が解った。

解説
ツールカウントしているのは破線の数(=ページ数)だ。
mincore()とstatは違う線を数えているので値が合わない事がある。
mincore()はmapfileが物理メモリーに乗ってるか否か?を知りたいのでこの実装で正解だろう。
実際ページにアクセスする際も(割り込みは入るが)PTE上の処理で終わるので高速(minor page fault)

カーネルコード(知りたい人だけ)

/proc//statm

residentデータ取得

  • task_statm()
  • (get_mm_counterはmm_structのメンバーを取るだけのマクロ)
  • なのでresidentはanonymousを取って返すだけ。

データを作ってる部分も近くにあった

               :
	if (pte_present(ptent)) {
		page = vm_normal_page(vma, addr, ptent);
	} else if (is_swap_pte(ptent)) {
		swp_entry_t swpent = pte_to_swp_entry(ptent);

		if (!non_swap_entry(swpent))
			mss->swap += ptent_size;
		else if (is_migration_entry(swpent))
			page = migration_entry_to_page(swpent);
	} else if (pte_file(ptent)) { 
                // ※ ここに流れてくるとpage = NULL のままなのでanonymous のカウントアップは行わない
		if (pte_to_pgoff(ptent) != pgoff)
			mss->nonlinear += ptent_size;
	}

	if (!page)
		return;

	if (PageAnon(page))
		mss->anonymous += ptent_size;
               :
mincore()

かたやmincore()ではmincore_pte_range()で同様の処理が走るがpte_present(pte)の場合は勿論、pte_file(pte)の場合もカウントアップしている。

PTEに無くてもVMAマッピングまで見に行ってる。

これが差を生んでいる様だ。

         :
		if (pte_none(pte))
			mincore_unmapped_range(vma, addr, next, vec);
		else if (pte_present(pte))
			*vec = 1;
		else if (pte_file(pte)) {
                        // ※ vma->vm_file->f_mappingまで見てページカウント(*vec)している。
			pgoff = pte_to_pgoff(pte);
			*vec = mincore_page(vma->vm_file->f_mapping, pgoff);
		} else { /* pte is a swap entry */
			swp_entry_t entry = pte_to_swp_entry(pte);
         :

まとめ

MongoDBでminor page faultが起こるケースはmongod再起動位しか思いつかない。
なので、mongodが暫く仕事をすれば両者は合って来るだろう。
workingsetのpageInMemoryを信じて運用して構わない

4/17追記

と思ったのだが、やっぱりダメだ!!
信じちゃいかん!!!

実メモリサイズを数倍、数十倍と大きく超えて報告する現象が多発。。。
また後で調べる。。。