Linuxに関する性能問題に一緒に立ち向かってくれるperfという心強い味方

この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。

サマリ

  • perfというLinux性能問題に立ち向かうための強力なツールがあるのでみんな使おう!
  • 今回は自分のよく使うperf top、perf record、perf scriptについて紹介するよ!
  • manが詳しいので困ったらmanを読んだほうがいいよ!

背景

プログラムを作る上で、性能問題を回避することはできない。 自分のプログラムならトレースを仕掛ければいいんだけど、 OSが絡んでいたり、性能が出ないコードの書き方をしていたりするとトレースだけでは難しいこともある。 そんなとき、割と何でも見られるperfが役に立つこともあるかもしれない。

perfとは

Linux用のプロファイリング・トレーシングツール。 manを見ると"perf - Performance analysis tools for Linux"とあって、本当にそのままの名前。 ハードウェアのパフォーマンスカウンタを採取したり、カーネル内に仕掛けられたトレースポイントで 情報(カウントだけじゃなくトレースポイントに応じて)を取ったりできる。

perfのインストール

何も考えずにaptなりyumなりで入れられる。aptの場合はなぜかlinux-tools-genericに入っているので注意。自分でコンパイルしようとすると、Linuxカーネルソースが必要な上に、いろいろ依存ライブラリが多くて面倒なのでおすすめはしない。

# Ubuntuの場合
$ sudo apt install linux-tools-generic

# CentOSの場合
$ sudo yum install perf

とりあえずperf topで中を見てみる

perf topとは文字通りtopコマンドを関数単位で実行してくれるようなもので、プロファイリング結果をリアルタイムで覗けるツール。perf topをするとこんな感じ。 CentOSでいうところのdebuginfoが入っていないのでnodeやら何やらアドレス値しか見えないけど、debuginfoが入っているものや-g付きでコンパイルしたものは関数名が見える。

$ sudo perf top

f:id:nbisco:20161211184947p:plain

perf topはデフォルトでCPUのサイクルをいちばん食っている(要は時間)もの上位を出力してくれるけど、 もちろん違う指標でプロファイリングもできるし、コアやプロセス、スレッド狙い撃ちもできる。 よく使いそうなオプションはこの辺ですかね。

  • -C <cpu-list>:デフォルトでシステム全体のところを、コアを狙い撃ちすることができる。-C 0-3とか、-C 0,1,2,3みたいに使う
  • -p <pid>:PID指定。-p 0,1,2みたいに複数指定もできる
  • -t <tid>:TID指定。TIDはps auxww -Lで確認しよう
  • -u <uid|user_name>:uidもしくはuser_nameで指定したユーザが実行中のプロセスについて表示
  • -c <count>:イベントが<count>回発生するごとに情報採取
  • -f <freq>:プロファイリング周期
  • -e <event>:プロファイルするイベント。ハードウェアのパフォーマンスカウンタ(例えばサイクル数とかキャッシュミス数とか)とか、カーネル内のトレースイベントを指定できる。

-e について

どんなものが指定できるか?

perf listで指定可能なイベントはほぼ全部見られる。ここで出てきた名前をそのまま-eオプションに渡してやればいい。 親切な名前がついているので、おおよそ名前の通りのイベントが取れる。

$ perf list

List of pre-defined events (to be used in -e):

  branch-instructions OR branches  [Hardware event]
  branch-misses                    [Hardware event]
  bus-cycles                       [Hardware event]
  cache-misses                     [Hardware event]
  cache-references                 [Hardware event]
  cpu-cycles OR cycles             [Hardware event]
  instructions                     [Hardware event]
  ref-cycles                       [Hardware event]
<中略>
  L1-dcache-load-misses            [Hardware cache event]
  L1-icache-load-misses            [Hardware cache event]
  L1-icache-loads                  [Hardware cache event]
  LLC-loads                        [Hardware cache event]
  LLC-prefetch-misses              [Hardware cache event]
  LLC-prefetches                   [Hardware cache event]
  LLC-store-misses                 [Hardware cache event]
  LLC-stores                       [Hardware cache event]
  branch-load-misses               [Hardware cache event]
  branch-loads                     [Hardware cache event]
  dTLB-load-misses                 [Hardware cache event]
  iTLB-load-misses                 [Hardware cache event]
  iTLB-loads                       [Hardware cache event]
<以下略>

ハードウェアのイベントの指定方法について

"ほぼ全部"なのは、perf listには載っていないけどCPUのアーキテクチャ依存な直接指定できるイベントが存在するから。 man perf topを見ると、perf stat -e r1a8 -a sleep 1なんて例が載っているけど、CMASKとかINVとかの指定の仕方がわかりにくい*1ので、冗長だけどわかりやすい指定方法で指定すればいいと思う。 コアイベントだけじゃなく、uncoreイベントも同様に取れる。

# 以下の2つは同じ意味(のはず。CMASK指定してなさそうだし)。
# 最後のsleep 1は、1秒間だけ測定するときの定型句。
$ perf stat -e r1a8 -a sleep 1
$ perf stat -e cpu/event=0xa8,umask=0x1,name=LSD.UOPS,cmask=0/

ハードウェアのパフォーマンスカウンタに何があるか、どんな値を指定すればよいかは、IntelのDeveloper's Manual Vol.3Bを見よう。

perf recordとperf script

perf recordは文字通りハードウェアのイベント/ソフトウェアのトレースポイントでのイベントを採取してくれるもの。ソフトウェアのトレースポイントと言うのは、例えば、割り込みハンドラがいつ動いたかとか、どのプロセスからどのプロセスへコンテキストスイッチしたか、等々が該当する。

例として、結構よく取られそうなスケジューリング関連の統計情報イベントを取ってみる。 スケジューリング関係のイベントは、perf listで見たときに全部sched:というprefixがついているのでわかりやすい。 その中の統計情報イベントはsched:sched_stat_xxxxという形式で書かれている。

$ sudo perf list | grep sched: | grep stat
  sched:sched_stat_blocked              [Tracepoint event]
  sched:sched_stat_iowait               [Tracepoint event]
  sched:sched_stat_runtime              [Tracepoint event]
  sched:sched_stat_sleep                [Tracepoint event]
  sched:sched_stat_wait                 [Tracepoint event]
  sched:sched_stick_numa                [Tracepoint event]

スケジューリング関連の統計情報イベントを5秒間採取するには、以下のコマンドを打てばOK。 以下のコマンドを実行すると、カレントディレクトリにperf.dataというバイナリファイルができあがっている*2。イベント間にスペース入れると実行できなくなるので注意*3

# -aはシステム全体を情報採取対象とするオプション、-Tはタイムスタンプをつけるオプション
$ sudo perf record -T -a -e sched:sched_stat_blocked,sched:sched_stat_iowait,sched:sched_stat_runtime,sched:sched_stat_sleep,sched:sched_stat_wait,sched:sched_stick_numa -- sleep 5

perf.dataができたら、perf scriptで中身を見てみよう。いっぱい行があって、"なるほど、わからん"という気分だと思う。

            perf 25665 [000] 10703.314496: sched:sched_stat_runtime: comm=perf pid=25665 runtime=668711 [ns] vruntime=4029076279864 [ns]
            perf 25665 [000] 10703.314522: sched:sched_stat_runtime: comm=perf pid=25665 runtime=26659 [ns] vruntime=4029076306523 [ns]
       rcu_sched    10 [000] 10703.314524: sched:sched_stat_runtime: comm=rcu_sched pid=10 runtime=2203 [ns] vruntime=4029072613356 [ns]
       rcu_sched    10 [000] 10703.314525: sched:sched_stat_runtime: comm=rcu_sched pid=10 runtime=1190 [ns] vruntime=4029072614546 [ns]
           sleep 25666 [000] 10703.314970: sched:sched_stat_runtime: comm=sleep pid=25666 runtime=444092 [ns] vruntime=4029073055245 [ns]
           sleep 25666 [000] 10703.315009: sched:sched_stat_runtime: comm=sleep pid=25666 runtime=39401 [ns] vruntime=4029073094646 [ns]
         rcuos/0    11 [000] 10703.315042: sched:sched_stat_runtime: comm=rcuos/0 pid=11 runtime=32519 [ns] vruntime=4029072643672 [ns]
         rcuos/0    11 [000] 10703.315043: sched:sched_stat_runtime: comm=rcuos/0 pid=11 runtime=1479 [ns] vruntime=4029072645151 [ns]
       rcu_sched    10 [000] 10703.315044: sched:sched_stat_runtime: comm=rcu_sched pid=10 runtime=1413 [ns] vruntime=4029072615959 [ns]
       rcu_sched    10 [000] 10703.318039: sched:sched_stat_runtime: comm=rcu_sched pid=10 runtime=5552 [ns] vruntime=4029072621511 [ns]
       rcu_sched    10 [000] 10703.318040: sched:sched_stat_runtime: comm=rcu_sched pid=10 runtime=2378 [ns] vruntime=4029072623889 [ns]
         rcuos/0    11 [000] 10703.318047: sched:sched_stat_runtime: comm=rcuos/0 pid=11 runtime=6431 [ns] vruntime=4029072651582 [ns]
      memballoon  8171 [000] 10703.512849: sched:sched_stat_runtime: comm=memballoon pid=8171 runtime=67601 [ns] vruntime=4029072678754 [ns]

おおよそ察しはつくかもしれないけど、単にperf scriptを実行したときのsched_stat_runtimeのフォーマットはこんな感じ。

<プロセス名> <PID> [<コア番号>] <タイムスタンプ>: <イベント名>: comm=<実行中コマンド名> pid=<PID> runtime=<RUNNING状態だった時間> vruntime=<Linuxのスケジューラがスケジューリングに使う仮想的な時間>

runtimeは(たぶん)RUNNING状態だった時間であって、プログラムが動作し続けていた時間と必ずしも等しいわけじゃないことに注意されたし*4。vruntimeはスケジューラが使う仮想的な時間なので見てもあまり意味がない*5

perf scriptにオレオレスクリプトでデータ整形してもらう

perf scriptはベタに結果を出力する機能以外にも、自分で作った整形スクリプト(言語はPerlもしくはPython)を使ってくれるという便利機能がある。 手順は以下の3ステップ。

  1. perf scirpt -g <python|perl>で雛形を作ってもらう
  2. 雛形を書き換える
  3. perf script -s <自分の作ったスクリプト>で集計を実行してもらう

例えば、runtimeの合計と平均を出すにはこんな感じ。

gist.github.com

さいごに

perfは大変便利な道具ですが、道具に振り回されないようにがんばる必要がありますね。

*1:というかどう指定すればいいかよくわからん

*2:出力先は-o filenameで指定することもできる

*3:僕は5分位はまった

*4:割り込み処理中はRUNNING状態から遷移しないので、RUNNING状態だけど動作していない場合がある

*5:実時間がniceで重み付けされた時間になっているはずだけど、これがどうということはあまりない、かもしれない

Windowsに必ず入れるソフト

この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。

サマリ

最近Windowsを新しくインストールする機会があり、せっかくなので備忘録的に何を入れたか残しておく。

一覧とちょっとしたコメント

  • ブラウザ:Firefox(ツリー型タブとBeyond Australisのせいで捨てられない)
    • Autopagerize
    • Beyond Australis
    • ツリー型タブ
    • FireGestures
    • はてブ
    • Omnibarの開発が止まっていたけど、代替品としてBeyond Australisが使えるのでChromeに引っ越さなくてもよくなった。大変うれしい。
  • 動画:VLC
  • 音楽:iTunes
    • Macは捨てられてもiTunesを捨てるのはしばらく先になりそう。
  • ファイラ:As/R
    • 機能をほとんど使いこなせていないけど、手に馴染んでてよい。
  • ランチャ:Launchy
    • マウスを極力使わなくてよくなるのでよい。
  • テキストエディタ:Mery
    • 軽くて気軽に使えるのがよい。
  • クリーナ:CCleaner
    • これなんで使ってるんだろうなあ・・・よくわからなくなってきたよ・・・
  • 現像:Lightroom
    • これがあるからwindowsにしているといっても過言ではない。
  • 画像ビューア:MassiGra
    • 軽くてよい
  • IMEGoogle日本語入力
    • これ実はMS-IMEでもいいような気がするけど、今回もいれてしまった。
  • PuTTYsshクライアント

まとめ

ちゃんとした記事を書くには時間がいる。毎日何か書くのは大変だなあ・・・

OpenIndianaから脱出してUbuntuに戻った話(その2:ソフトウェアの準備)

この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。

サマリ

  • ひよってUbuntu 16.04 LTSをいれてしまった。
  • サーバの名前は食べ物の名前にするという伝統を受け継ぎ、新NASのhostnameはmochiにした。

OSは何を入れたか

半年くらい前にマウスコンピュータで安めのノートPCを買って、Arch Linuxを放り込んで遊んでいたらすっかり慣れてしまったし*1、困ることも特段なかったのでArch Linuxでもよいかなと思ったけども、安定稼働という幻想が頭をよぎってUbuntu 16.04にした。基本的にはインストーラに従っていけばよいのは楽でいい。標準+OpenSSHサーバでインストールした。

どうせmdでソフトウェアRAIDにするし、今思えばArch Linuxにしてもよかったのではという感じだ。 CentOSDebianはちょっと古めなのが気になるし、Fedoraは常に崩壊しているという偏見がある。GUIがいらなかったのでManjaroとMintは選外となった。

サーバの名前はmochiにした。

必需品のインストール

このあたりは淡々と進めればよい。

# 必需品。vimは入っていた
% sudo apt install zsh tmux vim nmap sysstat git

# dotfilesのcloneとインストール
% git clone git@github.com:bisco/dotfiles.git
% cd dotfiles
% ./install.sh


# 引越用にcifs-utils
% sudo apt install cifs-utils

# 最近はntpdを使わないらしい
% sudo apt install chrony
% sudo vim /etc/chrony/chrony.conf

chrony.confの変更抜粋。nictはsinet経由になるから遅延がちょっと大きい、だそうだ。

<20行目付近>
#pool 2.debian.pool.ntp.org offline iburst
server ntp1.jst.mfeed.ad.jp offline iburst
server ntp2.jst.mfeed.ad.jp offline iburst
server ntp3.jst.mfeed.ad.jp offline iburst

<70行目付近>
#allow foo.example.net
#allow 10/8
#allow 0/0 (allow access by any IPv4 node)
#allow ::/0 (allow access by any IPv6 node)
allow 192.168.0.0/24

chrony.confを変更後、リブートしても起動するようにしておく。

% chronyc sources # 一応同期
% sudo systemctl restart chrony
% sudo systemctl enable chrony

mdでソフトウェアRAID

ZFSほどではないが、mdでソフトウェアRAIDするもの十分簡単。 mdでデバイス作ったあとはinitramfsを更新しておかないと、次回/dev/md0にならなくて/dev/md127になってしまい、 fstabに不用意に書いてると泣くことになるので注意。 仮に忘れたとしても、/dev/md0じゃなくて/dev/md/<hostname>:0みたいに名前でfstabへ書いておけばOK。

# まずはドライブをGPTでフォーマット。fdiskはMBRでフォーマットしてしまい、2TB以降を認識できなくなるので注意。
# sdaとsdbあわせて2回分やる
% sudo parted /dev/sda
(parted) mklabel gpt
(parted) mkpart primary 1M 4001GB
(parted) p # 念のため確認
(parted) q

# mdでRAID1にする
% sudo mdadm --create /dev/md0 --level=raid1 --raid-devices=2 /dev/sd[ab]1

# 設定を保存する
% sudo mdadm --detail --scan | sudo tee /etc/mdadm/mdadm.conf  

# ちゃんとinitramfsを更新する(僕は忘れてひどいめにあった)
% sudo update-initramfs -u

# かたそうなXFSにする
% sudo mkfs.xfs /dev/md0

# マウントポイントを用意
% sudo mkdir /mnt/md0

# ユーザが直接さわれるように777にしておく
% sudo chmod 777 /mnt/md0

# fstabに書き込む。 nofailにしておくと仮にこけてもブートしてくれる
echo "/dev/md/mochi:0 /mnt/md0 xfs nofail 0 0"  | sudo tee -a /etc/fstab 

Chinachu + Mirakurun on Docker

最初は普通に入れようと思ったけど、nodejsのバージョンが複雑怪奇でつらかったのでDocker版を入れた。 Dockerもaptで入るものだと古くてダメだったという落とし穴があった。 Chinachu on Dockerのインストール方法は以下を見てね。 qiita.com

受信できるチャンネル番号がわからない、そんなときは脳筋棍棒ソリューションで何とかしよう。 recpt1コマンドをぶん回して、受信できたっぽいものを設定すればよいのだ。

% docker exec -it mirakurun /bin/bash
bash-4.3# for i in {10..64}
do
echo "Channel $i" && recpt1 --b25 --strip --device /dev/pt3video2 $i 1 "/dev/null" && echo ""
done

なお、自分の環境だと、Chinachu + Mirakurun on Dockerを使ってTVtestでテレビを見ようとすると 途中で接続が途切れてしまうんだけどなんでだろう・・・ Windowsから見てるとMirakurunからパケットが飛んでこなくなってるように見えるけど・・・ BonDriver_Mirakurunをちゃんとビルドしなきゃダメかな・・・

Sambaの設定

Windowsとしか共有しないので、Sambaを入れる。 unix password = syncにすると勝手にユーザを同期してくれるかと思いきやそんなことはなく、手動でいれる必要があった。

% sudo apt install samba

# 設定ファイルを更新する
% sudo vim /etc/samba/smb.conf

# Sambaにユーザを追加する
% sudo smbpasswd -a <ユーザ名>

smb.conf(以下、一部抜粋)はこんな感じにした。インターネット各地に設定項目がちらばってたので結構苦労した。 面倒でもmanを読むべきだった。

[global]
# Character encoding
    unix charset = UTF-8
    dos charset = CP932

# Windowsから\\mochiでアクセスできるように
    netbios name = mochi

# bindするnetworkは固定し、さらに自宅内からのみアクセス可にする
interfaces = 127.0.0.1 192.168.0.0/24
bind interfaces only = yes
hosts allow = 192.168.0.0/24 127.0.0.1

# Sambaユーザのパスワード変更とLinuxユーザのパスワード変更を連動させる
unix password sync = yes

# 自分だけ入れる共有フォルダを作る
[share]
  path = /mnt/md0/share
  writable = yes
  create mode = 0644
  directory mode = 0775
  valid users = bisco
  force group = bisco
  force user = bisco
  acl allow execute always = yes

まとめ

mochiは今もちゃんと動いています。いまだにChinachu + Mirakurun on Dockerでのリアルタイム視聴はできないけど、録画はできているっぽいのでよしとする。 なんかいつの間にか直って困惑しているがともかく直ったのでよしとしよう!!

*1:常に最新に近いものが使えるのはとてもよかった

OpenIndianaから脱出してUbuntuに戻った話(その1:ハードウェアの準備)

この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。

サマリ

OpenIndianaに嫌気がさしたので、UbuntuNASを組みなおした。

はじめに

OpenIndianaが更新されなくなって久しい。 当時の僕はZFSをどうしてもネイティブな環境で使いたくて、OpenIndianaを使ってNAS環境を作った。しかし、だがしかし、更新されないのはいい加減につらくなってきたし、たまにしかさわらないので使い慣れないし、たまに使ったら使い慣れてないせいでストレスたまるし、なんか最近ハードウェアの調子悪そうだし・・・などいろいろ重なったので、思い切ってNAS環境を作り直すことにした。

NASの材料

材料はこんな感じ。静かで小さなのがほしかったので、Mini-ITXでそろえた。 Mini-ITXは小さすぎてあまりドライブが積めないので、豪華に4TBのディスクを2本買ってRAID1にした。 本当は3~4本かってRAID5くらいにするのが費用対効果を考えるとちょうどいいと思う。

  • A1SAi-2550F(Intel Atom C2550, SoC, FCBGA 1283, 14W 4-Core)
  • メモリ8GB
  • Lian Li PC-Q21 シルバー
  • SilverStone SST-ST30SF V2(SFX 300W)
  • Crucial MX300 CT275MX300SSD1
  • HGST HDN724040AL(3.5" HDD 4TB 7.2k) x2
  • PT3

組み上げ時の1枚

Mini-ITXで組み上げるのはパズルを解くのに近い感覚があり、やれHDDが入らないとか、やれHDDの電源ケーブルが届かないとかでものすごく苦労した。HDD電源ケーブルに至っては付属品だと全く届かなかったので、あわててヨドバシの通販で延長コードを買うなんていうこともした。ともかくMini-ITXマザーボードとケースは小さく、僕の指はにんじんのように太いので、何度ねじを外したかわからない。

この写真を見ると何となくわかってもらえると思う。もはやマザーボードは見えやしないし、HDDなんてかすかにいることがわかる程度だ。

f:id:nbisco:20161207005844j:plain

OSのインストールとかはまた明日・・・

手間がかからずおいしい朝ごはんを目指して

この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。

サマリ

  • おいしい朝ごはんを楽して食べたい
  • 冷凍チャーハンのコスパがよいのでおすすめ
  • 栄養面で改善の余地が大いにあるのでなんとかしたい

はじめに

ひとり暮らしの僕にとって、朝ごはんは頭の痛い問題である。具体的には以下の3つ。

  • 問題点1:できるだけ長い時間うとうとしていたいのに朝ちょっと早起きしなきゃいけない
  • 問題点2:自分ひとりのために作るのが面倒*1
  • 問題点3:コンビニなりパン屋なりで買うと結構高くつく(コンビニで買うものは味が濃いので毎日食べられない問題もある)

いろいろ問題はあるにせよ、朝ごはんを食べたいことには変わりないので、 何とかしてこの辺を解決する方法を見つけたい。

朝ごはんの要件

問題点から、朝ごはんの満たす要件は以下の2つになる。以降、以下の要件を満たす朝ごはん案について、実際に試したことがあるものについて述べる。

  • 要件1:手早く、簡単に、おいしいものが用意できる(問題点1、2に対応)
  • 要件2:コンビニなりパン屋で買うよりもお金がかからない(問題点3に対応)

案1:シリアル

シリアルのよいところ

今回言うところのシリアルは、フルーツグラノーラもしくはオールブランとする*2。 牛乳をかければ即食べられるという、手軽さの面で他の追随を許さないところがよい。 甘みがあり食べやすいのもポイント。 栄養強化もされてるっぽいので栄養面でも死角はないのではないかと思うけども、実際はよくわからない。

価格の面でも有利であり、1食換算すると30円くらいになるんじゃなかろうか(1袋500円で、2週間くらい持ったような気がする)。

シリアルの悪いところ

いつかフルーツグラノーラの甘みに耐えられなくなる。何度かフルーツグラノーラ生活をしているが、 僕はだいたい3か月〜半年くらいで耐えられなくなってくる。 オールブランとか甘くないでしょというのは確かにそうだが、それでも多少は甘みがある。 甘みがないと食べられないが、甘いだけだと辛くなるジレンマは解決の見通しが立たない。

案2:手作りおにぎり

手作りおにぎりのよいところ

米を炊いたものを冷凍しておけば、翌朝解凍して、具を混ぜるなりふりかけをかければ立派なおにぎりのできあがりである。 解凍までは3分ほどだし、具を混ぜたりふりかけをかけるだけだが作る喜びも多少は味わえるし、 何より米を食べられるのがよい*3。水気もあるので単体でもいける。

手作りおにぎりを試行するにあたり、試しに鯖フレークを買ってみたが、これが大変おいしく、 単体でむしゃむしゃ食べてしまったことは大いに反省している。

価格の面では、まあそこそこいいんじゃないですかね。コンビニでおにぎりを買うよりは相当安いよ多分。

手作りおにぎりの悪いところ

米を炊くときに増える洗い物がダメ。 1回炊くと7個おにぎりが作れるので、1回作るとしばらく洗わなくてもいいんだけど、 洗い物が増えるという心理的負担に耐え切れずに挫折。 おにぎりが悪いわけではなくて、何か洗い物があんまり好きじゃない僕のせい。 もちろんやるときはやるし、洗い物を無限にためたりも絶対しないけど、 炊飯釜とか炊飯器蓋とか、大きめのものはできれば洗いたくないなという気持ちでいっぱいになるので ダメだった。

あと、あえて言うならもっとおかずを追加して栄養バランスを取ったほうがよい。あえて言うなら。

案3:お茶漬け

お茶漬けのよいところ

ごはんを解凍して、お湯をかけるだけなので楽ちんだし、お茶漬けの素もそんなに高くないのでコスパもよい。

お茶漬けの悪いところ

手作りおにぎりの問題に加えて、飽きてしまうのが問題。 味を変えながら2週間くらい食べ続けたところで、お茶漬けに飽きてしまった。はて、何でだろう。

案4:冷凍焼きおにぎり

冷凍焼きおにぎりのよいところ

手作りおにぎりはかなりいい線まで行ったが、洗い物をしたくないという心理的ハードルに破れた。 今回は大きな洗い物をしなくてよいというところで死角がなくなった。

価格は8個入り500円なので、手作りおにぎりよりも多少高いと思うが、 コンビニおにぎりほどではないので特に問題なし。

冷凍焼きおにぎりの悪いところ

これも手作りおにぎりと同じで、栄養バランスがよくないのがいまいち。 あとは、味が単調になるのでいつか飽きそうな点だけど、 同じ味の焼きおにぎりを2週間くらい食べ続けてみたところ、2週間くらいなら大丈夫だった。 お茶漬けとの違いは全くわからないが、何となく歯ごたえに原因がありそうな気がする。

案5:冷凍チャーハン

冷凍チャーハンのよいところ

焼きおにぎりのよいところとほぼ同じ。400gで250円で、だいたい1週間ちょいくらい持つので、冷凍焼きおにぎりよりもコスパはよい。 冷凍のくせに味もよいので、今は冷凍チャーハン生活をしている。

冷凍チャーハンの悪いところ

焼きおにぎりに同じ。栄養バランス取りたい。

まとめ

冷凍チャーハンは安くて味もよく、飽きにくいのでおすすめ。 しかし、栄養バランスが明らかにわるいので、何か野菜とたんぱく質が取れるおかずを追加したほうがよい。

[参考]突撃となりの朝ごはん

同期やら先輩やらに、朝ごはんに何を食べているかを聞いた結果を以下にまとめる。 やはりごはん食よりも手軽そうなパン食が多いっぽい。 冷奴というのは結構独創性があってよいと思ったが、まだ試せていない。

  • カロリーメイトとかそれに類するもの
  • コンビニで買ったパン
  • コンビニで買ったおにぎり
  • ホームベーカリーで手作りしたパン
  • パン屋で買ったパン
  • 冷奴
  • 栄養ドリンクとウィダーインゼリー
  • 焼いた食パンとベーコンエッグ
  • ベーコントースト(食パンの上にベーコンを載せてトースターで焼く)

[参考]僕の理想の朝ごはん

参考までに、僕の理想の朝ごはんメニューはこんな感じ*4。 そこそこ以上のホテルの朝ごはんはおおよそ条件を満たしてくれる上に、片付けをしなくていいので幸せにひたれる。

  • ふっくら炊きあがり、水気をやや多めに含んだ白米
  • だしのきいた具だくさんの味噌汁
    • 豆腐、わかめ、小口切りしたねぎが基本。にんじん、ごぼう、大根を追加した豚汁は最高。たまねぎを入れて甘くしてもよいし、溶き卵を入れてボリュームと見た目を強化するのもいい。
    • 味噌は白味噌もしくは白味噌多めの合わせ味噌がいい。赤味噌は嫌いではないけれど、甘めのものが好き。
  • 焼き魚
    • 塩鮭、鯖、アジの開き、さんまの塩焼き(蒲焼きでもいい)などなど、脂が乗ってて骨ごと食べられる魚がベスト。
    • うるめいわしやししゃもも渋くてよいが、あまり見かけない
  • 漬物
    • 例えば、ふっくらした明太子、ごりっと歯ごたえのあるごぼうの味噌漬け、山形名産のだし、ぱりっとしたたくあん、酒粕で漬けた瓜などが挙げられる
  • ふわっとしただし巻き卵(半熟の目玉焼きでもいい)
  • ベーコンと野菜の炒めもの(野菜を取るのが趣旨なのでサラダで代替してもよい)

パンも好きだが、ごはん派です。

*1:他人がいたら作るのかというとそれは別の問題

*2:他のは食べてない

*3:僕は米好き

*4:何の参考だよ

"pyokkorin feat. Kagamine Rin!" is so classic

この記事はぴょこりんクラスタ:Re Advent Calendar 2016のために書いたものです。

サマリ

pyokkorin feat. Kagamine Rin!に動画をつけました。この動画が全てで、あとの文章は全ておまけ。

www.nicovideo.jp

背景

まずはこれをみてほしい。お風呂に入りながら携帯ではてブを眺めるという あれなアクティビティに勤しんでいたときに見つけた動画だ。

www.nicovideo.jp

僕はこれを見て、「すごい!生きてる!!!」と 今思えばよくわからない理由で感動しすぎて文字通りぐずぐずに泣いた。 感動のあとは、自分も同じくMMDで何か作ってみたいという衝動が湧いてきたわけだけど、 MMDをダウンロードして使い方をざっと調べたところで何をしたらいいかよくわからなかった。 よくあるダウンロードしてチュートリアルしたら満足してしまう病気だ。

そんなところで今年もピョッコリンさん主催でアドベントカレンダーをやることになった。 まがりなりにも2年間続いたので、せっかくなら去年のアドベントカレンダーとの間に連続性を持たせたい。 去年のピョッコリンアドベントカレンダーのおさらいをしたところ、 ちょうどいい感じに12/24の回にピョッコリンさんが曲をリリースしていたので、その曲にMMDダンスをつけた。

練習作ということで、リンちゃん*1にはジャージで踊ってもらった。 MVにジャージはどうなのよということも考えたけれど、踊っているときに手が衣装にめり込みにくく、 ひらひらしてない分長い手足の動きが目立つ(ような気がする)という利点があり、 さらには表情の動きがとてもいい感じにつけられる初心者にやさしいモデルなので、 衣装云々に関してはあまり気にしていない。これはこれでかわいらしいと思う。

振り付けは冒頭の初月 is so Classicを作った@Somubu_choさんの作ったものから 何となくリズムがあっているLove Shakeを選んだ。 本当は初月 is so Classicのダンスを当てたかったけど、 リズムが全然合わない上に、それを強引に合わせこむ技量がなかったのでリリース優先でがんばることにした。

せっかくなので苦労した点

音楽とダンスを合わせるのが一番大変だった*2。 合わないのはわかる、でも何がずれているから合わないように見えるのかがわからなくて苦労した。 結局のところ、ダンスはステップが全てで、音楽のリズムとステップが合ってれば だいたいうまくいってるように見えるけど、ステップが合わなければ何をしてもダメ。 その辺は偉大な先人が踊っているのをひたすら眺めてたら気づいた。 Michael Jackson - Thriller - YouTubeと、 Pulp Fiction - Dance Scene (HQ) - YouTubeはどれだけ見ても飽きないですね。

あとはニコニコ動画にアップロードするときのffmpegオプション。 なかなか覚えられないし今後はHandbrakeみたいにかんたんなやつでやろう*3

*1:今までどちらがリンでどちらがレンなのか区別ついてない不届き者だったのでリンちゃんと呼ぶと怒られてしまうかもしれない。さんをつけろよデコ助野郎的なあれ。

*2:口パクは諦めてもいいやと思えるくらいに達するまでにはあまり時間がかからなかった

*3:今度はないかもしれない

kill_fasyncの引数のまぎらわしさ

tl;dr

  • kill_fasyncの第2引数は飛ばしたいシグナル番号を指定するわけではない
  • つまみ食いで勉強すると落とし穴にはまる危険があるので気をつける

詳細

kill_fasyncという関数をご存知だろうか。kill_fasyncとは、Linuxカーネルが提供する関数の1つで、 カーネルモジュールからユーザプロセスへシグナルを飛ばすものだ。

Kernel 4.8での実装はこんな感じ。至ってシンプルで、 正直これで何がわかるか、というところであるが、少なくとも何をいくつ引数に取るかわかる。

Linux/fs/fcntl.c - Linux Cross Reference - Free Electrons

724 void kill_fasync(struct fasync_struct **fp, int sig, int band)
725 {
726         /* First a quick test without locking: usually
727          * the list is empty.
728          */
729         if (*fp) {
730                 rcu_read_lock();
731                 kill_fasync_rcu(rcu_dereference(*fp), sig, band);
732                 rcu_read_unlock();
733         }
734 }
735 EXPORT_SYMBOL(kill_fasync);

さて、これを見て何を思うか。素直な心と有り余る読解力で読むと、第2引数のsigは飛ばすシグナルの番号だと思うのではないだろうか。 ところがこれは飛ばすシグナルの番号とは全く関係がない。

さて、kill_fasync_rcuを見てみよう。

699 static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
700 {
701         while (fa) {
702                 struct fown_struct *fown;
703                 unsigned long flags;
704 
705                 if (fa->magic != FASYNC_MAGIC) {
706                         printk(KERN_ERR "kill_fasync: bad magic number in "
707                                "fasync_struct!\n");
708                         return;
709                 }
710                 spin_lock_irqsave(&fa->fa_lock, flags);
711                 if (fa->fa_file) {
712                         fown = &fa->fa_file->f_owner;
713                         /* Don't send SIGURG to processes which have not set a
714                            queued signum: SIGURG has its own default signalling
715                            mechanism. */
716                         if (!(sig == SIGURG && fown->signum == 0))
717                                 send_sigio(fown, fa->fa_fd, band);
718                 }
719                 spin_unlock_irqrestore(&fa->fa_lock, flags);
720                 fa = rcu_dereference(fa->fa_next);
721         }
722 }

意外にもsigはSIGURGじゃなければOKで、実際にシグナルを飛ばしていると思しき send_sigioにはsigが全く入っていない。じゃあ一体sigって何なのか。 それに飛ばしたいシグナル番号ってどこに入ってるんだ。

順番が前後するが、飛ばしたいシグナル番号はsend_sigio関数を見ればわかるよね。 send_sigioはさらにsend_sigio_to_taskという関数を呼んでる。

491 void send_sigio(struct fown_struct *fown, int fd, int band)
492 {
493         struct task_struct *p;
494         enum pid_type type;
495         struct pid *pid;
496         int group = 1;
497         
498         read_lock(&fown->lock);
499 
500         type = fown->pid_type;
501         if (type == PIDTYPE_MAX) {
502                 group = 0;
503                 type = PIDTYPE_PID;
504         }
505 
506         pid = fown->pid;
507         if (!pid)
508                 goto out_unlock_fown;
509         
510         read_lock(&tasklist_lock);
511         do_each_pid_task(pid, type, p) {
512                 send_sigio_to_task(p, fown, fd, band, group);
513         } while_each_pid_task(pid, type, p);
514         read_unlock(&tasklist_lock);
515  out_unlock_fown:
516         read_unlock(&fown->lock);
517 }

あった。シグナル番号signumはfownに入ってる。

449 static void send_sigio_to_task(struct task_struct *p,
450                                struct fown_struct *fown,
451                                int fd, int reason, int group)
452 {
453         /*
454          * F_SETSIG can change ->signum lockless in parallel, make
455          * sure we read it once and use the same value throughout.
456          */
457         int signum = ACCESS_ONCE(fown->signum);
458 
459         if (!sigio_perm(p, fown, signum))
460                 return;
461 
462         switch (signum) {
463                 siginfo_t si;
464                 default:
465                         /* Queue a rt signal with the appropriate fd as its
466                            value.  We use SI_SIGIO as the source, not 
467                            SI_KERNEL, since kernel signals always get 
468                            delivered even if we can't queue.  Failure to
469                            queue in this case _should_ be reported; we fall
470                            back to SIGIO in that case. --sct */
471                         si.si_signo = signum;
472                         si.si_errno = 0;
473                         si.si_code  = reason;
474                         /* Make sure we are called with one of the POLL_*
475                            reasons, otherwise we could leak kernel stack into
476                            userspace.  */
477                         BUG_ON((reason & __SI_MASK) != __SI_POLL);
478                         if (reason - POLL_IN >= NSIGPOLL)
479                                 si.si_band  = ~0L;
480                         else
481                                 si.si_band = band_table[reason - POLL_IN];
482                         si.si_fd    = fd;
483                         if (!do_send_sig_info(signum, &si, p, group))
484                                 break;
485                 /* fall-through: fall back on the old plain SIGIO signal */
486                 case 0:
487                         do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group);
488         }
489 }

でもfown_structなんてどこで設定するんだ?と思ったあなたは鋭い。 実はkill_fasyncを使うには(当たり前だが)準備がいる。 具体的にはfcntlを使えばよい。以下のスライドを見てくれれば何となくわかると思う。 F_SETSIGとシグナル番号を与えてfcntlを撃ってやればOKだ。先に見るべきだった。

http://www.cs.usfca.edu/~cruse/cs635s05/lesson22.ppt

では、SIGIOは一体何なのか。 このへんを読むと、非同期のI/Oイベントを伝えるときにはSIGIOを使うらしいというのがわかる。 要はある種のお約束的なものですね。 まあ確かにカーネルモジュールというかドライバだから、ユーザプロセスに何か通知するのって、 外部デバイスの入出力イベント以外にないよなあ。

ところでSIGURGって何で特別扱いされてるの?と気になるけど、こちらもある種のお約束的なものらしい。 SIGIOよりも用途が限定されてるようなので特別扱いなのかも。

ソケットを使用したアプリケーションへの信号

SIGURG は、OOB データをサポートしているソケットでアウト・オブ・バンド (OOB) データを受信するときに送信される信号です。たとえば、AF_INET アドレス・ファミリーで SOCK_STREAM タイプのソケットは、 SIGURG 信号を送信するように条件付けることができます。
SIGIO は、あらゆるタイプのソケットで通常のデータ、OOB データ、エラー条件、その他が発生したときに送信される信号です。 

まとめ

kill_fasyncの第2引数はある種のおまじない的にSIGIOを指定すればよいっぽい。 使い方をちゃんと勉強しないと余計なことを調べなきゃいけなくなるので、 ショートカットせずに地道に調べた方がよい。