【続】フリーズの原因は"kswapd0"と共有メモリだった話

1. 概要

フリーズの原因を探ると"kswapd0"というカーネルスレッドと、共有メモリのリソース大食いだったことがわかった。
前回の記事を書いてからも3週間あまり調査を続けてきたが、32bit版のサポート終了に伴い、調査を終了する。
よって、この2つの原因について、わかったことをまとめる。


2. 前回までのおさらい

  • Google ChromeおよびFirefoxで動画視聴中に画面がフリーズする
  • カーソルは動くが、ブラウザを閉じるなどの操作を受け付けない
  • リソースモニターを表示していても、CPU使用率が上昇が確認されない
  • 再起動すれば回復する
同じ症状の報告がなく、ブラウザのアップデートでも解決しなかった、
このことから、原因は不明のまま、特定の環境で発生すると結論づけた。

3.環境

  • LinuxMint17.2 Cinnamon 32bit
    • Linux Kernel 3.16.0-38-generic
  • CPU : Core2Duo E8500(3.16GHz)
  • Mem : DDR2 2GB*2
  • VGA : Radeon HD5750

4. GUIでなくCUIならどうなるか

人間がコンピュータを使うためにはGUIは欠かせない存在だ。
同時に、GUIを利用するためにはそれなりに多くのリソースが必要になる。
もし、フリーズする原因がGUIのリソース大食いだとすれば、CUIに切り替えることで原因がわかるかもしれない。

仮想コンソールはCtrl+Atl+F1(F6まで使用可)で起動できる。
そして、「CPU使用率が100%になってるんじゃないか」という想像は正しかった。
"topコマンド"を実行したところ、"kswapd0"というプロセスがCPU使用率100%になっていた(図1)。
これだ!やっと犯人を見つけた。

図1.仮想コンソールでのtopコマンドの結果
※この時点ではメモリ不足に気づいてない。

私はCPU使用率やメモリ使用率の動向を見るのが好きで、10年以上リソースを監視するソフトを使ってきた。
その経験から、「フリーズする際はだんだんCPU使用率が高くなる」ことが常識だったのだが、今回はそのパターンには当てはまらなかった。

なお、時間経過では解決しなかった。
システムは完全にハングアップしてしまっている。
何か方法を探すのが懸命だろう。

5. "kswapd0"とはなにか

"swap"と名前がついている通り、空きメモリを確保できるように頑張るのが仕事だ。

ということは、メモリが足りていないという前提があるハズだ。
しかし、フリーズした画面では2.5~3GB余っている。
メモリは余っているのに、"kswapd0"が動く。
これはどういうことだろうか?

5.1)メモリの開放を試す

メモリが足りないという可能性を確かめるために、メモリを開放してみることにした。
フリーズが解消されれば、メモリの開放が正しいといえる。

メモリを開放するにはsysctlコマンドを使う。
sysctlコマンドはカーネルのパラメータを変更する。
具体的には次のように書く。
$sudo sysctl -w vm.drop_caches=3


[!]Note:
(スペルに注意。"cache"は「貯蔵庫」「かくし場所」を意味し、"cash"は「現金」を指す)

値は1〜3を指定できるようだ。
「3」を指定しているのは、「1」と「2」の両方の効果が得られるからだ。
それぞれの値については参照してほしい。
仮想コンソールに入ってコマンドを実行するも、フリーズは解消されなかった。

メモリを開放するコマンドが、実はもう一つある。
"echo"で値を書き換える方法だ。
例:echo 1 > /proc/sys/vm/drop_caches

この方法は多くのページで紹介されている。
試したが、そのままでは「許可がありません」とエラーが返される。
正しく実行するにはシェルスクリプトであることを明示する必要がある。
$sudo sh -c 'echo 1 > /proc/sys/vm/drop_caches'

なんでこんなことを書いたかって、私がシェルスクリプトだと知らずに見事に失敗したからだ。orz


6. ノーマークだった共有メモリ(shared)

topコマンドを実行することで、CPU使用率の異常をつきとめた。
そして、"freeコマンド"で黒幕を見つけることができた。

freeコマンドを実行すると、メモリの使用状況を見ることができる。
コマンドにはメガバイト単位で表示するためのオプション「m」をつける。
これだけでは一度しか実行されないので、「s30」で30秒毎に走らせる。
$free -ms30


画像を見てもらった方が理解が早いだろう。
この画像はフリーズする直前に撮ったものだ(図2)。

図2.リソースモニターとfreeコマンドの空きメモリの違い


項目がいくつかあるが、一般的にメモリ使用率を見る場合は、2行目の"used"と"free"の項を見る。
リソースモニターで表示されるメモリ使用率はこれだ。
この場合、1GBが使用中で、およそ3GB余っていることになる。

一応、計算してみよう。
2行目の使用済み(used)は、1行目の"used"から(buffers)+(cached)の値を引いたもの。
2行目の空きメモリ(free)は、1行目の"free"から(buffers)+(cached)の値を足したものだ。

(例)
988(used) ≒ 3731(used) - {5(buffers) + 2736(cached)} = 990(MB)
3057(free) ≒ 315(free) + {5(buffers) + 2736(cached)} = 3056(MB)

「なーんだ。メモリは余ってるじゃん」と考えていた。
ちょっと待ってほしい。
1行目の"free"が315MBしかない。

そして、隣の"shared"は2447MB(2.4GB)も使用している
ここでピンときた。

"shared"が肥大化し、使えるメモリが少なくなったために"kswapd0"が動き出したのではないか。

これで2つがつながった。
キャッシュを開放するコマンドが効かないわけだ。
共有メモリを開放する効果がないのだから(図3)。

図3.drop_cachesでメモリを確保しようにも共有メモリには効果がない


私が「ノーマーク」と表現したのは、例えばfreeコマンドから空きメモリを計算するときに"shared"にまったく触れられていないからだ。

次の目的は、共有メモリを開放することになる。

[!]Note:
Linuxはメモリをよく使うように設計されている。
悪い意味ではなく、「使わないでいるよりかは、使った方が効率がいい」からだという。
だから、"free"が少なくなるだけでは、メモリ不足ではない。


7. 共有メモリを開放したい

"shared"は共有メモリの使用量を表している。
共有メモリとは、文字通り「複数のプラグラムで共有するためのメモリ」だという。
共有メモリは正しく動作しているのだろうか。

7.1)"shared"の謎

freeコマンドを調べていると、わからないことが増えた。
「sharedは昔のなごりで、今は使われていない」や「sharedは無視してよい」「0である」とするページがある。
カーネル2.6以降は使用されないと書いているところもある。
疑うわけではないが、明確なソースを見つけられなかった。
現に、LinuxMint17.2のカーネルは3.16で、共有メモリが肥大化している。


しかし、「共有メモリの値が異常」とするトラブルは見つからなかった。
ということは、「本来、共有メモリがこれほどまで肥大化することはない」といえる。
実際、問題がない場合は(Chrome + ニコニコ生放送(Flash)) + (Firefox + evernote)で執筆をしながら"shared"の値は60MBを維持している。
PC起動時は26MBだ。

監視のためにfreeコマンドを30秒ごとに走らせていたが、ひどいときは30秒で50〜100MBずつ共有メモリが肥大化していく
"shared"が2000MB以上、"free"が300MBを下回ると高確率でフリーズする。
困ったことに、目に見えるプログラムをすべて閉じ、30分ほど放置してもメモリは開放されない。
(一応、リソースモニターで「すべてのプロセス」を表示させ、Chromeなどの終了を確認している)

数十分〜数時間でコンピュータが動かなくなるなんて信じられない。
異常だと考えるのは自然だろう。


7.2)共有メモリの開放を試す

結果からいえば、ここで紹介するコマンドではメモリを開放することができなかった。

ipcsコマンドと「m」オプション、「um」オプションで共有メモリの使用状況を確認できるようだ。
また、ipcrmコマンドにIDを引数として渡すことでメモリを開放できる。
しかし、まったく効果がなかった。

また、共有メモリの総和をメガバイトで計算したが、107MBしかない(図4)。
図4.共有メモリの総和をバイト→キロバイト→メガバイトに変換
さっぱりわからず、ここが限界だと悟った。


8. 今回のまとめ

  • フリーズの原因は"kswapd0"というプロセスだった
  • "kswapd0"が動き始めるのはメモリ不足が原因
  • リソースモニターなどでは、メモリが余っているように「見える」
  • 共有メモリが以上に肥大化し、コマンド・時間経過で解消されない
  • 解消するには再起動するしかない
前回の記事からわかったこと
  • FirefoxやOperaでもフリーズしたと書いたが、共有メモリが異常な値だったのでフリーズしやすい状態だった
  • ログアウトでは解消されない
    • ログアウトすることでChromeが終了し、その分だけメモリが開放されたにすぎない

9. おわりに

長く問題に取り組み、得られたものもあったが、このトラブルについては満足のいく解決策が見つけられなかったのが悔やまれる。
フリーズの問題はとにかく手ごわかった。
画面がカクつく前兆から、素早く仮想コンソールに入る必要があった。
そうでなければ、パスワードを入力するためにプロンプトが返ってこず、再起動するハメになる。

納得のいかないことも多い。
freeコマンドの"shared"が動いている理由もわからない。
2016年の2月に入ってから急にフリーズが多発したこともだ。

怪しいのがChromeかFlashか、という点は変わらない。
だが、これ以上の検証は無意味だろう。
3月4日にはGoogle Chrome 49がリリースされたが、すでに32bit版へのサポートは終了している。
新たにダウンロードすることもできなくなった。

このPCで戦うのも限界なので、4月にはパーツを変えるつもりだ。
だが、「共有メモリが肥大化する」問題が解消されなければ、メモリを増やしたところで意味がない。
そこだけが不安だ。

+α.補足

Q.グラフィックドライバは関係あったの?
A.No
AMDの提供するプロプライエタリドライバ(fglrx : FireGL and Radeon X)では表示に問題があったのですぐに戻した。
ただし、プロプライエタリからデフォルトに戻す場合は、チェックを入れるだけでは不十分だ。
xorg.confを削除する必要がある。

Q.PCのスペックが貧弱なんじゃないの?
A.No
LinuxMint17の推奨スペックでは、メモリは1GBあればいい。
(17.2の情報を見つけられなかったので17.3のもの)
また、実メモリは4GBで、いつものようにchrome + Flashを動かしても1GBで済んでる。
PCのスペックが貧弱とは思えない。

Q.swap領域を用意してないからじゃないの?
A.No
Linuxでswap領域が必要なのは一昔まえのような、物理メモリが少ない場合だと認識している。
4GBで充分だと思っているし、メモリが2GBを超えるのはkritaで大量のレイヤーをもった画像を処理するときだけだ。
また、swap領域を作ってしまうと、SSDのアクセスを増えてしまうおそれがある。

Q.tmpfsのサイズが問題じゃないの?
A.No
SSDにLinuxMint17.2をフルインストールした記事で、私はtmpfsを使用している。
初期値で物理メモリの半分を割り当てていたが、tmpfsは余った領域を他のアプリケーションに譲ることができる。
dfコマンドで確認したところ、tmpfsの使用率は1%以下だった。
念の為、最大値を50%の2GBから64MB、128MBまで縮小したが、フリーズ問題には関係なかった。

Q.ChromeのFlashプラグインの種類に問題あるんじゃないの?
A.No
プラグインにはNPAPIとPPAPIの2種類がある。
切り捨てられたNPAPIはCPUに依存し、PPAPIはGPUに依存するようだ。
手元の環境ではChromeにバンドルされたPPAPIしか入っておらず、種類の問題とは無関係だ。