元号の処理がどこで行われているか探してみる
この記事はLinux Advent Calendar 2016の4日目の記事です。
わりとマイナーな機能ですが、linuxでja_JP.utf8のlocaleで以下のようにコマンドを叩くと、元号をちゃんと処理して表示してくれたりします。
$ LC_TIME=C date +'%EY'
2016
$ LC_TIME=ja_JP.utf8 date +'%EY'
平成28年
ちょっと前に国の象徴のおじいさん(82)が「退位したい」みたいなこと言ってましたし、いいかげん年なのでしんどかろうと思います。法律の要件とかはわかりませんが退位が行われれば元号が変わるでしょうから、庶民の我々も少なくともこのコードを修正しないといけないわけです。
これはどこからきているのかを探りましょう。
$ ldd `which date`
linux-vdso.so.1 (0x00007ffd14593000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fea550ed000)
/lib64/ld-linux-x86-64.so.2 (0x000056070c37e000)
シンプル。linux kernel, dateコマンドの中身, libcのどれかに絞られました。
kernelはありえないので候補から外します。心配な人はstraceでもして安心してください。
date本体もないと思っているのですが念の為確認します。
$ LC_TIME=ja_JP.utf8 ltrace date +'%EY'
(中略)
strftime(" \345\271\263\346\210\22028\345\271\264", 1024, " %EY", 0x7f8f5c1464a0) = 12
fwrite("\345\271\263\346\210\22028\345\271\264", 11, 1, 0x7f8f5c142600) = 1
(以下略)
libcのstrftimeがやっているようです。man 3 strftime を見ます。
(ー略ー)
%E Modifier: use alternative format, see below. (SU)
(ー略ー)
(SU) The Single UNIX Specification mentions %Ec, %EC, %Ex, %EX,
%Ey, %EY, %Od, %Oe, %OH, %OI, %Om, %OM, %OS, %Ou,
%OU, %OV, %Ow, %OW, %Oy, where the effect of the O modifier
is to use alternative numeric symbols (say, roman numerals), and
that of the E modifier is to use a locale-dependent alternative
representation.
あ、Single UNIX Specificationで決まってたのか。ってことはきっとAppleのOS Xとかでもつかえるんだろうな。。持ってないので知らないけど。
さて、libcでlocale-dependentだということがわかりました。ソースコードとってきましょう。
$ find glibc-2.24 |grep ja
./localedata/locales/ja_JP
./benchtests/strcoll-inputs/lorem_ipsum#ja_JP.UTF-8
./po/ja.po
./debian/po/ja.po
ということで以下のファイルに含まれてそうです。
glibc-2.24/localedata/locales/ja_JP
いかにもそれらしい「era」というエントリがあります。きっとこれなんですが……
era "<U002B><U003A><U0032><U003A><U0031><U0039><U0039><U0030><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U002B><U002A><U003A><U5E73><U6210><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
"<U002B><U003A><U0031><U003A><U0031><U0039><U0038><U0039><U002F><U0030><U0031><U002F><U0030><U0038><U003A><U0031><U0039><U0038><U0039><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U5E73><U6210><U003A><U0025><U0045><U0043><U5143><U5E74>";/
"<U002B><U003A><U0032><U003A><U0031><U0039><U0032><U0037><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0038><U0039><U002F><U0030><U0031><U002F><U0030><U0037><U003A><U662D><U548C><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
"<U002B><U003A><U0031><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0032><U0035><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U662D><U548C><U003A><U0025><U0045><U0043><U5143><U5E74>";/
"<U002B><U003A><U0032><U003A><U0031><U0039><U0031><U0033><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0032><U0034><U003A><U5927><U6B63><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
"<U002B><U003A><U0032><U003A><U0031><U0039><U0031><U0032><U002F><U0030><U0037><U002F><U0033><U0030><U003A><U0031><U0039><U0031><U0032><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U5927><U6B63><U003A><U0025><U0045><U0043><U5143><U5E74>";/
"<U002B><U003A><U0036><U003A><U0031><U0038><U0037><U0033><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0031><U0032><U002F><U0030><U0037><U002F><U0032><U0039><U003A><U660E><U6CBB><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
"<U002B><U003A><U0031><U003A><U0030><U0030><U0030><U0031><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0038><U0037><U0032><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U897F><U66A6><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
"<U002B><U003A><U0031><U003A><U002D><U0030><U0030><U0030><U0031><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U002D><U002A><U003A><U7D00><U5143><U524D><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>"
ソースコードのくせに謎エンコードされてて読めないよ! >_<
きっとunicodeの表が頭に全部はいっててデコードできる人は心の目で読めるんですがそんなハンドアセンブルの100倍くらいしんどそうなスキルは持っていません。ちなみにハンドアセンブルも表がないとできません。
しょうがないので<Uxxxx>; みたいなのが来たらxxxx部分にあたるunicode文字に置換する使い捨てスクリプトをシコシコ書きます。
import os, re
def main():
for line in file('ja_JP'):
out = ""
while 1:
m = re.search('(<U([0-9a-fA-F]+)>;?)', line)
if m:
out = out + line[:m.start()] + unichr(int(m.group(2), base=16))
line = line[m.end():]
else:
out = out + line
break
print out.encode('utf8'),
main()
これを実行すると.. でました
era "+:2:1990/01/01:+*:平成:%EC%Ey年";/
"+:1:1989/01/08:1989/12/31:平成:%EC元年";/
"+:2:1927/01/01:1989/01/07:昭和:%EC%Ey年";/
"+:1:1926/12/25:1926/12/31:昭和:%EC元年";/
"+:2:1913/01/01:1926/12/24:大正:%EC%Ey年";/
"+:2:1912/07/30:1912/12/31:大正:%EC元年";/
"+:6:1873/01/01:1912/07/29:明治:%EC%Ey年";/
"+:1:0001/01/01:1872/12/31:西暦:%EC%Ey年";/
"+:1:-0001/12/31:-*:紀元前:%EC%Ey年"
元年だけ別の行になってるんですね…… 明治は6年からなので旧暦のごちゃっとしたあたりは気にしないと。
というわけで元号の場所はわかりました。おじいさんが退位したらここを直してみんなでglibcをアップデートしよう。
ではでは。
追記
「別ファイルになってないの?」と聞かれたので追記 。
たとえば /usr/share/i18n/locales/ja_JP のように別ファイルになっていて、localedefを使うことで独自のlocaleを定義することもできる。
でもlocaleの設定がおこなわれるのはプロセスの最初のほうで setlocale() されるタイミングだから結局元号の処理更新を反映したいプロセスの再起動が必要なのは変わらないのでした……。
OOM Killerにであったら何をするべきか?
OOM killerで大事なプロセスが殺される。困りますね。。 google で OOM Killerと入力すると 「無効」とか補完されます。しかしどうするのが良いのか、あまりよく説明されている記事がみあたらなかったので自分の考えをメモしておきます。
OOM Killer の目的は何か?
まずは何故OOM Killerが発生しているのかについて、ざっくりイメージをつかみましょう。linux kernelはプロセスからの「メモリくれ」という要求に対してたぶん足りそうだという場合に「OK」といって渡します。実際のメモリ割り当てはアクセスが発生するタイミングまで遅延させます。これを遅延アロケーションといい、だいたいにおいてうまく動きます。ただし必ずうまくいくと保証されているわけではないので破綻することがあります。
OOM Killerはこの遅延アロケーションが破綻しそうなときに、適当にプロセスを殺すことでメモリを開放してやろう、そうやってメモリを開けて動作を継続したほうが全滅するよりはマシだろうという緩和策です。
「半端に動くんじゃなくて死んでくれた方がいいです」という場合には、 sysctl で以下のように設定して、OOM Killerを動作させるのではなくカーネルをpanicさせることができます。
vm.panic_on_oom = 1
OOM Killerは破綻したときの緩和策だったので、OOM Killerを止めて破綻をより直接的にシステム停止に繋げています。きっちり死んでくれるのでOOM Killerよりわかりやすいですね。
何故OOM Killerが発生しているかを調査する
できればOOM Killerを避けたいですが、避けるためには何故OOM Killerが発生しているのかを調査する必要があります。これをおこなうためには、まずさきほどのpanic_on_oom設定をおこないます。
加えてpanic時にメモリダンプを取得する仕組み(いろいろありますがRHELやFedoraだとkdump)を仕込みましょう。これでpanicしたときのダンプを調査することで、多くの場合OOM Killerの発生原因を推定できます。
対処その1. アプリケーションに対してストレージを速くする
メモリにはざっくり3種類あります。
- ファイルやブロックデバイスをバックエンドとするfile mapped page
- バックエンドはなく、必要になるとswap領域にバックエンドを作るanonymous page
- 特に使われていないfree
linuxでは基本的にはディスクやファイル、swap領域に対するキャッシュとしてメモリは扱われます。そうではなくメモリ上に必ず維持されるようにする仕組み(mlock)もありますがそれが気になる人はこんなblog読まなくて大丈夫なはずなので割愛します。
アプリケーションが何かしらストレージへ書きだすとき、まずは書き出すべき情報をメモリ上に保持します。このようなメモリをDirtyと呼びます。
Dirtyなメモリはストレージへの反映による永続化が必要な情報を持っているので、ストレージに書きだしをおこなうまでは消せません。恒常的にストレージの負荷が高いとDirtyなメモリがどんどん増えていきます。
さきほどのメモリダンプを解析ツールに食わせると、dirtyなページが多くなっているのですぐわかります。このような場合、対処としては以下があります
- ストレージの障害をチェックする。何かデグレードが発生して遅くなっている可能性を排除しましょう。 高いストレージだとストレージ側で統計情報がとれます。これも確認しましょう。
- ストレージを増強する。根本対策です。
- アプリケーションの書きだしを遅くする。dirtyなページを書きだすプロセスの実行を遅くします。
アプリケーションがdirect I/Oとよばれる手法を利用している場合に限られますが、cgroupで制限することができます。systemdのunit内にBlockIOWriteBandWidth=で利用する帯域幅の上限を設定することができます。指定された上限にを越えて書きこみをしようとするとアプリケーションは一時停止します。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/sec-Modifying_Control_Groups.html#sec-Modifying_Unit_Files - 書きだしを遅くする(その2)。共有ストレージを持つクラスタ構成の場合、ストレージに対しての計算ノード数を減らします。
- 書きだしを遅くする(その3)。メモリがでかいときはvm.dirty_ratioを変更したりすると良いケースもあります。これはdirtyなページが何パーセント溜まったら、新規に書きだしを行おうとしているプロセスを一時停止するかの指標です。
- メモリを増やす。ストレージへの負荷が適度に増減する場合、次に負荷が減るまでなんとか持てばいいので、その余裕がある時間をかせぎます。場合によりますが、メモリがとても少ない場合をのぞいてあまり有効な対応ではありません。
対処その2. 積極的にswapさせる
利用しているアプリケーションのメモリ管理があまりうまくできていないと、実際の利用はそれほどでもないのにanonymousとして確保するメモリ量がじわじわ増大していきます。このような場合、アプリケーションによりますがswapを積極的に行うことも一つの手です。
sysctlで、以下のようにするとRHELではfile mappedと同じ程度の基準でanonymousメモリをswapに吐きだします。
vm.swappiness = 100
ゆっくりとしたメモリ消費増加への対策としてはswapはそれなりに有効です。もちろんswapがあふれると破綻しますから適宜swap使用量を監視する必要があります。
swapについてはとにかく発生させたくないと、拒否反応を示す人がいます。
「swapで遅くなる」という現象のほとんどは、1) anonymous pageを一旦swapへ書きだしてメモリから除いたあと、2) もう一度アクセスしようとしたときに発生します。
この2の動作をswap inと呼びます。vmstatだとsiと書いてある欄で表示されます。これが継続的に発生する場合、swapによるパフォーマンス劣化が発生していると考えてまず間違いありません。この場合、アプリケーションのメモリ利用パターンがswapの積極的な利用に向いていません。
swap inによるパフォーマンス劣化が継続的ではなくちょこちょこ程度で起きる場合、主要ではないアプリケーションがswap outしている可能性が考えられます。この影響を緩和したい場合「メモリを増設するのは高いのでswap領域をSSDにしてしのぐ」というアイデアには一考の余地があります。
利用しているアプリケーションに、ある程度予想がつくゆっくりとしたメモリ利用の増加がある場合にはswapと適当な頻度でのサービス再起動の組み合わせは有効です。ただしswap inが継続的に発生する場合はswapによる緩和策はあまり向いていません。
対処その3. アプリケーションのメモリ消費を制限する
swapが向いていないアプリや、メモリの大量消費が突発的に発生するアプリケーションもあります。後者は70%くらいバグな気がしますが、残念ながらそういうアプリケーションでも使わないといけないシチュエーションはあります。
そのような場合、cgroupを利用してアプリケーションのメモリ消費を制限することが有効です。systemdを利用しているディストリビューションでは、systemdがcgroupの設定を管理します。 RHELであれば以下に和訳ドキュメントがあるので読みましょう。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/index.html
てっとり早く答えにたどりつきたい人は以下ページを見て、MemoryLimitを設定しましょう。 これで、指定したサービスの消費メモリを制限できます。制限に触れるとOOM Killerが動作しますが、このcgroup内のプロセスだけを殺しますのでサービスだけが死にます。周りにあまり迷惑がかからないのはいいですね。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/sec-Modifying_Control_Groups.html#sec-Setting_Parameters_from_the_Command-Line_Interface
これに加えて、サービス停止時の再起動設定もsystemdのunitに書くことができます。ただし自動再起動の設定をするのは「大量消費→OOM Killer→再起動→大量消費→……」 のタイトなループに陥る可能性が排除できている場合に限定しましょう。このループが数日や数ヶ月など、許容できる場合なら
Restart=yes
とunitに書くことでsystemdが再起動の面倒もみてくれます。
対処その4. メモリを増やす
「さっきあまり有効でないって書いてたのに!?」と思うかもしれませんが、アプリケーションが必要とするメモリ量の見積りが単純に低すぎる場合があります。
dirtyなページがあまり多いわけでもなく、swapさせてもswap inが発生しつづける場合は、アプリケーションが必要としている量のメモリを用意できていない場合が考えられます。可能であればアプリケーションの処理を勘案して必要サイズを推定しましょう。
メモリ使用量を調査するためのツールについては以下ドキュメントに紹介されています。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Performance_Tuning_Guide/sect-Red_Hat_Enterprise_Linux-Performance_Tuning_Guide-Memory-Monitoring_and_diagnosing_performance_problems.html
メモリを大量消費するアプリケーションには、自身がどれくらいのメモリを消費するか調整するための設定が用意されていることが多いです。これを増減させて消費メモリ量の増減とパフォーマンスへの影響を観察すると落としどころがみつかるかもしれません。
HPCなどでは「でかいデータを扱うからでかいメモリが要るんだよ!」 みたいなケースもあります。こういうときは金と物理(メモリ)の力で殴るしかないです。
対処ではない: oom_score_adj
OOM killerの調整ということで検索すると名前がよくでてくるoom_score_adjですが、特定プロセスをOOM killerの対象外にしたり対象になる確率を上げたり下げたりということができます。
しかしOOM killerが発生した原因であるメモリ逼迫については何も改善しないので、あまり意味はありません。
特定のプロセスが死ななければ他がランダムに殺されても大丈夫でしょうか? 普通はランダムに殺されても問題ないというプロセスはほぼ無いはずです。不要なプロセスが存在するなら、特にscoreとかいじる前に単純に起動しないようにする方がいくらかマシです。
oom_score_adjを活用できるシチュエーションをどうにか考えだしてみると、
1) 大事なプロセスに関連したものについてはOOM killerの対象外にしておく
2) メモリ逼迫を検出するためのカナリア役として優先的に殺されるプロセス(無意味にメモリを確保する)を仕込んでこれを監視に利用
3) カナリア役が殺され次第できるだけ正常な終了手順で大事なプロセスを終了
というような仕組みを作り込む時くらいではないかと思います。OOM killerが動きだしたあとに大容量メモリを確保しているであろう大事なプロセスの終了処理がうまく動くかは非常に疑問ですが、終了処理が追加メモリをあまり使わず動作する場合には使えそうです。
まれに有効: vm.overcommit_memory設定によるovercommitの禁止
vm.overcommit_memoryによるovercommitの禁止は「ほとんど全てのアプリケーションが必要より多くメモリをリクエストしてくる」ため、非常に効率は悪くなりますがメモリ確保の要求量(割り当て量ではない)が特定の値を越えるとOOM Killerによるプロセスの終了が発生します。早く失敗することで延々処理したあと死ぬよりはマシなのではないか、という考え方ですね。
特定のアプリケーションがメモリのほとんどを占め、必要分ギリギリしか要求しないことが分かっている、なおかつ他のプロセスはほぼ動作していない。という前提が満たせる場合はいいのですが、なかなか厳しいです。実際のところアプリケーションによっては通常の利用時に消費されるメモリ量の数十倍から100倍くらいのメモリを要求することもごく普通にあります。極めて特殊な、アプリケーションを選ぶ対策と言ってよいかと思います。
おまけ
いろいろいじりたい人にはRHELのドキュメント「パフォーマンスチューニングガイド」がまずはおすすめ。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Performance_Tuning_Guide/chap-Red_Hat_Enterprise_Linux-Performance_Tuning_Guide-Performance_Features_in_RednbspHat_EnterprisenbspLinuxnbsp7.html
logitechのマウスから謎のイベントが発生してハマった話と暫定的な対策
今にいたるも正確な原因はつかめていないのだが、同じようなハマり方をする人がいるかもしれないのでメモ。
問題
1. 自宅および会社のX環境で、logitech(logicool)の M705, Performance MXを楽しく使っていたのだが、不定期にフォーカスの動き方がおかしくなる現象に見舞われた。
2. 現象が発生するとキーボードのフォーカスはAlt+tabなどで他のwindowに移動できるが、マウスのフォーカスはある1つのwindowに固定されたまま他のwindowに移動できない。
3. マウスの電源オフするかunifiedレシーバを抜くと現象がおさまって普通に動作する。
4. はじめはランダムに発生していたが電源いれるといきなり発生するようになった。
さて、現在のところ原因追及はできていない。だがXよりも下位の、usbhid以下のどこかがおかしいようだ。原因追及は4のほぼ常時発生するようになってから進捗した。
再現&観測手順
1. 他のマウスとM705を2本接続し、M705の電源を一旦切った状態で操作をはじめる
2. xev起動( xev | grep -A3 Button )
3. もうひとつのマウスで xevのwindow上にカーソルを持っていったのちM705の電源を入れると以下のようなイベント表示があらわれる。
ButtonPress event, serial 33, synthetic NO, window 0x3a00001,
root 0x2da, subw 0x0, time 520218479, (136,90), root:(1337,150),
state 0x0, button 20, same_screen YES
存在しない20番目のボタンが押されたというイベントだ。そのためこのウィンドウ内でマウスのドラッグがはじまった状態になり、マウスのフォーカスが失われない。(観察した範囲では20番ボタンに限らず2番ボタンなどのイベントも発生する)
なおなんらかの方法(電源offやunified receiverの接続断)で接続を切ると対応するButtonRelease eventが発生してドラッグ状態が解除される。おそらくこれはxinputのどこかの終了処理が行っているのだろう。発生したイベントに対応するボタンがあればそれをクリックすることでもButtonReleaseイベントが発生するので対策になる。
現象が再現している状態でxinputコマンドによりマウスの状態を見ると20番ボタンがdownになっていることでも確認できる。
$ xinput query-state 11
2 classes :
ButtonClass
button[1]=up
button[2]=up
button[3]=up
button[4]=up
button[5]=up
button[6]=up
button[7]=up
button[8]=up
button[9]=up
button[10]=up
button[11]=up
button[12]=up
button[13]=up
button[14]=up
button[15]=up
button[16]=up
button[17]=up
button[18]=up
button[19]=up
button[20]=down
button[21]=up
button[22]=up
button[23]=up
button[24]=up
ValuatorClass Mode=Relative Proximity=In
valuator[0]=1622
valuator[1]=1635
valuator[2]=0
valuator[3]=-3
暫定的な対策
solaarにより Side Scrolling を有効にすることでこの現象が発生しなくなった。
正直なところ原因をはっきりさせていないので、これで対策になっているのかもわからないのだが、とりあえず動いている。
暫定的な対策その2(??)
もろもろ未確認だが、solaarがデバイス検出時行っている初期化処理で何か起きてるような気がする。solaarを全く動作させずに利用していると対策になるかもしれない。
solaarは過去1年くらい平和に動いていたのと、新しく買ったマウスではsolaarで問題が発生しないので、マウスの中の何かしらの状態とsolaarがリバースエンジニアリングして喋ってるlogitech独自プロトコルのあわせ技で問題が発生している可能性が疑われる。。
実験:
- マウス接続 → solaar起動 → ボタン20down発生 → solaar終了→down継続→マウスoff/on→ボタン20down発生せず
- solaar起動 → マウス接続 → ボタン20down発生 → マウスoff/on→ボタン20down再度発生
根本的(?)な対策
logicoolのサポートに連絡して押していないボタンについて押したような動作があると相談したところ、故障が疑わしいということで対応していただけました。保証期間内だと相談するといいかもしれません。
おそらく関連するbugzilla
https://bugs.freedesktop.org/show_bug.cgi?id=77037
closeされちゃってるけどたぶんこの問題と似た話。「xinputで状態確認するとよさそう。俺んとこはドラッグ中のような状態になってた」とコメントを書くためにアカウントを取ろうとしたら失敗したのでメールで申請中……。
RHEL7でとったスナップショットにroot fsをロールバックするメモ
snapperで作ったスナップショットに戻したい
rootfs切り替え時の注意
snapperによって作成されるLVの属性を確認する
[root@localhost ~]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pool00 rhel twi-aotz-- 12.00g 10.85 18.55
root rhel Vwi-aotz-- 12.00g pool00 10.02
root-snapshot10 rhel Vri---tz-k 12.00g pool00 root
root-snapshot11 rhel Vri---tz-k 12.00g pool00 root
root-snapshot12 rhel Vri---tz-k 12.00g pool00 root
root-snapshot13 rhel Vri---tz-k 12.00g pool00 root
1 Volume type: thin (V)olume
2 Permissions: (w)riteable, (r)ead-only
3 Allocation policy: (i)nherited
4 fixed (m)inor ← なし
5 State: (a)ctive
6 device (o)pen
7 Target type: (t)hin; whereas snapshots of thin volumes using the new thin provisioning driver appear as (t).
8 Newly-allocated data blocks are overwritten with blocks of (z)eroes before use.
9 Volume Health: ← なし
10 s(k)ip activation: this volume is flagged to be skipped during activation.
dracutによるinitramfsで、root fsをmountする前に停止する
rd.break={cmdline|pre-udev|pre-trigger|initqueue|pre-mount|mount|pre-pivot|cleanup}
drop to a shell on defined breakpoint
lvmでの作業
lvm> lvrename rhel/root root-original
lvm> lvrename rhel/root-snapshot10 root
lvm> lvchange -k n rhel/root ← s(k)ip をはずす
lvm> lvchange -p rw rhel/root ← readwriteができるようにする
lvm> lvchange -k y rhel/root-original ← s(k)ipするようにする
lvm> lvchange -p ro rhel/root-original ← read only にする
lvm> exit
再起動後
[root@localhost ~]# snapper create
[root@localhost ~]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pool00 rhel twi-aotz-- 12.00g 11.15 24.41
root rhel Vwi-aotz-- 12.00g pool00 root-original 10.00
root-original rhel Vwi---tz-k 12.00g pool00
root-snapshot11 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot12 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot13 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot14 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot15 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot16 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot17 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot18 rhel Vri---tz-k 12.00g pool00 root-original
root-snapshot19 rhel Vri---tz-k 12.00g pool00 root
root-snapshot9 rhel Vri---tz-k 12.00g pool00 root-original
swap rhel -wi-ao---- 2.00g
[root@localhost ~]# snapper list
Type | # | Pre # | Date | User | Cleanup | Description | Userdata
-------+----+-------+--------------------------+------+---------+-------------+---------
single | 0 | | | root | | current |
single | 9 | | Mon May 18 18:15:10 2015 | root | | |
single | 19 | | Tue May 19 18:48:48 2015 | root | | |
元LVの削除
[root@localhost ~]# lvremove rhel/root-original
Logical volume "root-original" successfully removed
[root@localhost ~]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pool00 rhel twi-aotz-- 12.00g 11.07 22.66
root rhel Vwi-aotz-- 12.00g pool00 10.00
root-snapshot11 rhel Vri---tz-k 12.00g pool00
root-snapshot12 rhel Vri---tz-k 12.00g pool00
root-snapshot13 rhel Vri---tz-k 12.00g pool00
root-snapshot14 rhel Vri---tz-k 12.00g pool00
root-snapshot15 rhel Vri---tz-k 12.00g pool00
root-snapshot16 rhel Vri---tz-k 12.00g pool00
root-snapshot17 rhel Vri---tz-k 12.00g pool00
root-snapshot18 rhel Vri---tz-k 12.00g pool00
root-snapshot19 rhel Vri---tz-k 12.00g pool00 root
root-snapshot9 rhel Vri---tz-k 12.00g pool00
swap rhel -wi-ao---- 2.00g
RHEL7でスナップショットとり放題設定をためしたメモ
インストール
状態を確認
[root@localhost log]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 500M 0 part /boot
└─sda2 8:2 0 16.4G 0 part
├─rhel-swap 253:0 0 2G 0 lvm [SWAP]
├─rhel-pool00_tmeta 253:1 0 12M 0 lvm
│ └─rhel-pool00-tpool 253:3 0 12G 0 lvm
│ ├─rhel-root 253:4 0 12G 0 lvm /
│ └─rhel-pool00 253:5 0 12G 0 lvm
└─rhel-pool00_tdata 253:2 0 12G 0 lvm
└─rhel-pool00-tpool 253:3 0 12G 0 lvm
├─rhel-root 253:4 0 12G 0 lvm /
└─rhel-pool00 253:5 0 12G 0 lvm
sr0 11:0 1 3.6G 0 rom
[root@localhost log]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pool00 rhel twi-aotz-- 12.00g 9.38 5.18
root rhel Vwi-aotz-- 12.00g pool00 9.38
swap rhel -wi-ao---- 2.00g
[root@localhost log]# lvdisplay /dev/rhel/pool00
--- Logical volume ---
LV Name pool00
VG Name rhel
LV UUID vuooGK-VHow-JfAo-cPQb-Egf1-fCk4-qRpLqK
LV Write Access read/write
LV Creation host, time localhost, 2015-05-15 19:15:03 +0900
LV Pool metadata pool00_tmeta
LV Pool data pool00_tdata
LV Status available
# open 2
LV Size 12.00 GiB
Allocated pool data 9.38%
Allocated metadata 5.18%
Current LE 3072
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:3
lvcreate --permission r --snapshot --name snapshot rhel/root
snapper設定の作成
[root@localhost ~]# snapper create-config -f 'lvm(xfs)' /
さらに設定一覧が /etc/sysconfig/snapper にあるのでここに設定名が追加されます。
[root@localhost log]# snapper list-configs
Config | Subvolume
-------+----------
root | /
snapshot作成
[root@localhost log]# snapper create
[root@localhost log]# snapper list
Type | # | Pre # | Date | User | Cleanup | Description | Userdata
-------+---+-------+--------------------------+------+---------+-------------+---------
single | 0 | | | root | | current |
single | 9 | | Mon May 18 18:15:10 2015 | root | | |
[root@localhost log]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pool00 rhel twi-aotz-- 12.00g 10.00 5.73
root rhel Vwi-aotz-- 12.00g pool00 10.00
root-snapshot9 rhel Vri---tz-k 12.00g pool00 root
swap rhel -wi-ao---- 2.00g
スナップショットのマウント
.snapshotディレクトリ以下に適当にmountされます。
[root@localhost ~]# snapper mount 10
[root@localhost ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/rhel-root 12571648 1226516 11345132 10% /
devtmpfs 1931348 0 1931348 0% /dev
tmpfs 1941204 0 1941204 0% /dev/shm
tmpfs 1941204 8580 1932624 1% /run
tmpfs 1941204 0 1941204 0% /sys/fs/cgroup
/dev/sda1 508588 168516 340072 34% /boot
/dev/mapper/rhel-root--snapshot10 12571648 1193528 11378120 10% /.snapshots/10/snapshot
snapshot間の差分
[root@localhost ~]# snapper create -t pre
[root@localhost ~]# ls
anaconda-ks.cfg hoge lvmdump-localhost.localdomain-20150515103428.tgz
[root@localhost ~]# rm hoge
rm: remove regular empty file ‘hoge’? y
[root@localhost ~]# snapper list
Type | # | Pre # | Date | User | Cleanup | Description | Userdata
-------+----+-------+--------------------------+------+---------+-------------+---------
single | 0 | | | root | | current |
single | 9 | | Mon May 18 18:15:10 2015 | root | | |
pre | 10 | | Mon May 18 18:18:32 2015 | root | | |
[root@localhost ~]# snapper create -t post --pre-number=10
[root@localhost ~]# snapper list
Type | # | Pre # | Date | User | Cleanup | Description | Userdata
-------+----+-------+--------------------------+------+---------+-------------+---------
single | 0 | | | root | | current |
single | 9 | | Mon May 18 18:15:10 2015 | root | | |
pre | 10 | | Mon May 18 18:18:32 2015 | root | | |
post | 11 | 10 | Mon May 18 18:19:42 2015 | root | | |
[root@localhost ~]# snapper status 10..11
-..... /root/hoge
c..... /var/log/messages
c..... /var/log/snapper.log
差分対象の制限(フィルタ)
/var/log/*
[root@localhost ~]# snapper status 10..11
-..... /root/hoge
undo
[root@localhost ~]# snapper undochange 10..11
create:1 modify:0 delete:0
TODO
freeの出力が大幅改善された話
最近freeコマンドを叩くとこんな感じで出力されます。
$ free
total used free shared buff/cache available Mem: 20209620 3859396 6594188 502492 9756036 15323144 Swap: 32767996 0 32767996
availableってなんでしょう? そして -/+ buffers/cache の行がなくなっています。
その背景をちらっと紹介します。
最近linuxの3.14で/proc/meminfo に MemAvailable というフィールドが追加されました。RHEL7.0や6.6にもバックポートされています。(RHEL6.6では互換性に配慮してデフォルトではdisableされています)
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
/proc/meminfo: provide estimated available memory Many load balancing and workload placing programs check /proc/meminfo to estimate how much free memory is available. They generally do this by adding up "free" and "cached", which was fine ten years ago, but is pretty much guaranteed to be wrong today. It is wrong because Cached includes memory that is not freeable as page cache, for example shared memory segments, tmpfs, and ramfs, and it does not include reclaimable slab memory, which can take up a large fraction of system memory on mostly idle systems with lots of files. Currently, the amount of memory that is available for a new workload, without pushing the system into swap, can be estimated from MemFree, Active(file), Inactive(file), and SReclaimable, as well as the "low" watermarks from /proc/zoneinfo. However, this may change in the future, and user space really should not be expected to know kernel internals to come up with an estimate for the amount of free memory. It is more convenient to provide such an estimate in /proc/meminfo. If things change in the future, we only have to change it in one place. Signed-off-by: Rik van RielReported-by: Erik Mouw Acked-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds
このフィールドは「だいたいこのくらいはswap発生させずにアプリケーションがallcoateできそう」というメモリ量の推定です。
procps-ngに含まれるfreeコマンドがbuffer や cacheとして使われているメモリをfreeに足して表示する機能があった(過去形)ことも手伝って、これらを単純に全て「カーネルがパフォーマンス改善のために未使用メモリをうまいこと使っている」と見做してシステム管理している人が多くいます。しかしこの見方は正しくありません。buffer や cacheは非常に重要で、もしこれらが0になってしまえばシステムは全く稼動できないでしょう。どれくらい存在すれば十分なパフォーマンスがでるかも、アプリケーションの特性により非常に大きくかわります。
そこでもうちょっとましな推定値をつくろうということで導入されたのがMemAvailableです。現在の実装ではかならずしもカーネルの中でこの推定をする必要はないのですが、将来的にカーネルのメモリ管理のしくみが変わっても同じように利用できるよう追加されました。
さて、これにともなってprocps-ngも3.3.10で更新されています。このprocps-ng 3.3.10はRHEL7.1に含まれています。
availableフィールドを追加 https://gitorious.org/procps/procps/commit/ba6396f886f1a9911221e1c7c4b66dc75acb6948
MemAvailableがなくても同等の計算を他のフィールドからするfallback https://gitorious.org/procps/procps/commit/b779855cf15d68f9038ff1809db18c0788e9ae70
MemAvailableはあくまでざっくりした推定値なので、既存の統計情報を足したり引いたり適当な割り算をしたりして出しています。なので、その元になっているフィールドが提供されているlinux 2.6.27以降であれば同じような計算をしてavailableを出す機能です。
-----------------------------------------------------------
/* zero? might need fallback for 2.6.27 <= kernel
if (!kb_main_available) {
if (linux_version_code < LINUX_VERSION(2, 6, 27))
kb_main_available = kb_main_free;
else {
FILE_TO_BUF(VM_MIN_FREE_FILE, vm_min_free_fd);
kb_min_free = (unsigned long) strtoull(buf,&tail,10);
watermark_low = kb_min_free * 5 / 4; /* should be equal to sum of all 'low' fields in /proc/zoneinfo */
mem_available = (signed long)kb_main_free - watermark_low
+ kb_inactive_file + kb_active_file - MIN((kb_inactive_file + kb_active_file) / 2, watermark_low)
+ kb_slab_reclaimable - MIN(kb_slab_reclaimable / 2, watermark_low);
if (mem_available < 0) mem_available = 0;
kb_main_available = (unsigned long)mem_available;
}
}
-----------------------------------------------------------
もはや不要となった free: remove -/+ buffers/cacheの削除 https://gitorious.org/procps/vnwildman-procps/commit/f47001c9e91a1e9b12db4497051a212cf49a87b1
そういうわけで、freeコマンドの出力が劇的に改善されたのでした。
IPアドレスのvalid lifetime
ip addr とかで
3: em1:mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether d4:3d:7e:dc:c8:ad brd ff:ff:ff:ff:ff:ff
inet 10.64.193.115/23 brd 10.64.193.255 scope global dynamic em1
valid_lft 82874sec preferred_lft 82874sec
inet6 fe80::d63d:7eff:fedc:c8ad/64 scope link
valid_lft forever preferred_lft forever
みたいになっているとき、em1のアドレスに有効期限がついている。
valid_lft 82874secが有効期限の設定。
この期限が切れた時には、kworkerがアドレスを切り離す。
これを設定しているのは /usr/sbin/dhclient-script で、以下みたいなコードで設定している。
ip -4 addr add ${new_ip_address}/${new_prefix} broadcast ${new_broadcast_address} dev ${interface} \valid_lft ${new_dhcp_lease_time} preferred_lft ${new_dhcp_lease_time} >/dev/null 2>&1