kernel の統計

stats of kernel

ネコはここまで考えている なんて言う楽しい本を読んだ。動物心理学から読みとく心の進化って副題がついていた。よく、猫は3年の恩を3日で忘れるとか言われるけど、それって本当って疑問から、色々と調査実験した結果をかみ砕いて説明してる。

例えば、声から顔を思い浮べるのか、とか、ネコは、どこに、何が を思いだせると等。論文が発表されて世界に話題になり、CNNやらBBCの取材を受けたそうだ。みんなミステリアスな猫に魅了されているんですなあ。

で、本の巻末に論文の補足が載ってた。分析にはRを使ったそうだ。2項検定やらオイラーの知らないGLMパッケージを駆使したやつが使われていた。統計が物を言う世界なんですなあ。

オイラーの統計と言うと今迄は血圧ばかりであったけど、趣向を変えてOpenBSDのカーネルの成分分析をしてみる。

結論を先に出しておくね。色々なハードで動くカーネルがどういうオブジェクトから構成されているかを分析(おこまがしいですが)してみた。

前回、/etc/rcの最後で /usr/libexec/reorder_kernel が走って、カーネルをオブジェクトに分解し、それをランダムに配置組立してるって書いた。その分解されたオブジェクトのサイズを採取したんだ。(サイズとファイル名前を欲しい場合は、コメントの行のようにする)

vbox# cd /usr/share/relink/kernel/GENERIC.MP/
vbox# ls -l *.o | awk '{print $5}' > /tmp/i386
## ls -l *.o | awk '{printf("%8d  %s\n", $5, $9)}' 

これが結果。r64Sは、前回作った特製(縮小版)のriscv64ね。それ以外はGENERIC版。

           amd64   i386      r64    r64S
----------------------------------------
 N      :   2072   1641      872     495
 Min.   :    400    276      456     456
 1st Qu.:  39604  20384    63106   43200
 Median : 105792  46844   167356  101008
 Mean   : 186715  79313   252977  164147
 3rd Qu.: 320078 104044   351036  219828
 Max.   :1268064 810056  2772752 1270408
 Sd     : 182448  87519   295427  184376

まずRのパッケージ、 R-cran-psychを追加しておく。(at FreeBSD)

> library(psych)
> amd <- read.csv("amd64", header=FALSE)
> describe(amd)
   var    n     mean       sd median  trimmed      mad min     max   range
V1   1 2072 186715.1 182448.4 105792 163585.7 126317.5 400 1268064 1267664
       skew kurtosis       se
V1 1.235344 1.824532 4008.162
> summary(amd)
       V1
 Min.   :    400
 1st Qu.:  39604
 Median : 105792
 Mean   : 186715
 3rd Qu.: 320078
 Max.   :1268064

普通は統計情報と言うとsummaryになるけど、残念な事に母数(N)と標準偏差(Sd)が出てこない。これは明らかに欠陥と思うぞ。で、余計なパッケージpsychを入れてdescribe関数を使うんだけど、これも勝手が悪い。

そこで合わせ技で、summaryの前後にN,Sdを追加。そうして出来たやつをクリッピング。後はpasteコマンドで結合。体裁を整るんで、emacsのレクタングル編集、C-x r d 削除機能を使った。

R以外はどうよ。

by octave

-- : statistics (X)
-- : statistics (X, DIM)
    Return a vector with the minimum, first quartile, median, third
    quartile, maximum, mean, standard deviation, skewness, and kurtosis
    of the elements of the vector X.

maximaにも統計関数が有ったように思うけど、忘却の彼方だな。それより身近にあるやつ (決してPythonなんて事は、オイラーの場合有馬泉)。

by gnuplot

お手軽。

gnuplot> stats "amd64"
  Records:           2072

  Mean:          186715.1351
  Std Dev:       182404.3961
  Sample StdDev: 182448.4286
  Skewness:           1.2362
  Kurtosis:           4.8172

  Minimum:          400.0000 [ 229]
  Maximum:       1.26806e+06 [1109]
  Quartile:       39600.0000
  Median:        105792.0000
  Quartile:      320676.0000

でも、やっぱりグラフでしょ。簡単に箱ひげグラフで概要把握。

gnuplot> plot 'r64S' with boxplot

ふーむ、世の中の縮図っぽいな。イーロン・マスクやらアマゾン親父みたいな富豪がいるかと思えば、貧困でやせ細っているファイルも多数。

sakae@pen:/tmp/REL$ sort r64S -n >z
 :
gnuplot> plot 'z' w l

データを並びかえておいて、それをグラフしてみると、二次関数ぽく表示された。

gnuplot> set logscale y; plot 'z' w l

片対数グラフにしてみた。結構広い範囲において、グラフがほぼ直線になった。やはり二次関数ぽい分布になってると思えるな。

こんな事をやってると、ソースの行数とオブジェクトファイルの大きさに相関は有るか、 とか、同一オブジェクト・ファイルを取出した時、64Bitバージョンと32Bitバージョンで相関は有るか、 なんて事を調べたくなるものだ。

行の長さの分布

同じ発想の人がおられたので、指を咥えて眺めておく。

reorder_kernel.sh

次はカーネルのランダム化がどのように行われているか、興味津々で調べてみたい。シェルスクリプトだった。概要は下記。

KERNEL=$(sysctl -n kern.osversion)
KERNEL=${KERNEL%#*}
KERNEL_DIR=/usr/share/relink/kernel

cd $KERNEL_DIR/$KERNEL
make newbsd
[ -f /etc/bsd.re-config ] && config -e -c /etc/bsd.re-config -f bsd
make newinstall
sync

echo "\nKernel has been relinked and is active on next reboot.\n"
cat $SHA256

もう少し詳しくって事でトレース、 sh -x reorder_kernel 更に、ログファイルが作成されるので、その一部 relink.log

+ sha256 -C /var/db/kernel.SHA256 /bsd
(SHA256) /bsd: OK
+ cd /usr/share/relink/kernel/GENERIC.MP
+ make newbsd
LD="ld" LDFLAGS="-g" sh makegap.sh 0xcccccccc gapdummy.o
ld -T ld.script -X --warn-common -nopie -o newbsd ${SYSTEM_HEAD} vers.o ${OBJS}
text    data    bss     dec     hex
13031367        229896  1130496 14391759        db99cf
mv newbsd newbsd.gdb
ctfstrip -S -o newbsd newbsd.gdb
rm -f bsd.gdb
mv -f newbsd bsd
+ [ -f /etc/bsd.re-config ]
+ make newinstall
install -F -m 700 bsd /bsd && sha256 -h /var/db/kernel.SHA256 /bsd
+ sync
+ echo \nKernel has been relinked and is active on next reboot.\n

掘り出し物ってかお宝発見。gdbにかけられるカーネルが作られていたよ。GENERICなもので好ければ、このセットが使えるな。前回みたいに苦労してカーネルを作る事はなかったわい。

vbox# ls -ltr
-rw-r-----  1 root  wheel     10752 Sep 28 03:41 smc93cx6.o
-rw-r-----  1 root  wheel      1150 Sep 28 03:41 makegap.sh
-rw-r-----  1 root  wheel      3440 Sep 28 03:41 locore0.o
-rw-r-----  1 root  wheel       436 Sep 28 03:41 gapdummy.o
-rw-r-----  1 root  wheel    135420 Sep 28 03:41 Makefile
-rw-r-----  1 root  wheel     13788 Sep 28 03:41 pcdisplay_subr.o
 :
-rw-r-----  1 root  wheel      1748 Sep 28 03:54 swapgeneric.o
-rw-r-----  1 root  wheel      4259 Sep 28 03:54 ld.script
-rw-r-----  1 root  wheel     69552 Sep 28 03:54 ioconf.o
-rw-r--r--  1 root  wobj      30832 Oct 27 02:41 ukbd.o
-rw-r--r--  1 root  wobj       2108 Oct 27 02:42 vers.o
-rw-rw----  1 root  wheel       634 Nov 24 05:41 gap.link
-rw-rw----  1 root  wobj      20104 Nov 24 05:41 gap.o
-rw-rw----  1 root  wheel     19376 Nov 24 05:41 lorder
-rwxrwx---  1 root  wobj   77267468 Nov 24 05:41 newbsd.gdb
-rwxrwx---  1 root  wobj   15214971 Nov 24 05:41 bsd
-rw-r--r--  1 root  wheel       507 Nov 24 05:41 relink.log

所で、再配置ってどうやってる? makegap.shが毎回走ってランダム牲を作り出す。

random_uniform() {
  :
        echo `jot -r 1 0 $_upper_bound 2>/dev/null`
}

RANDOM1=`random_uniform $((3 * PAGE_SIZE))`
RANDOM2=`random_uniform $PAGE_SIZE`
 :
cat > gap.link << __EOF__
  :
        .bss : {
                . = . + $RANDOM5;       /* fragment of page */
                . = ALIGN(16);
                *(.bss .bss.*)
        } :bss
}
__EOF__

$LD $LDFLAGS -r gap.link $GAPDUMMY -o gap.o

jot -r でランダムデータを作る。それらのデータで、リンカーの配置をランダム化。更に念を入れて、ランダムなサイズのオブジェクトファイルを作成。それをカーネルの一部にする事により、配置が予測出来無いようにしてる。

more stats

最初にやった統計には、重大な欠陥がある事に気付いてしまった。その、きっけけは、relink.logに出て来た、ctfstripってコマンド。70年の人生で初めて出会うコマンド。なにかと思ってしらべたら、デバッグ情報だけを取り除くものだった。普通のstripと何が違う?

vbox$ ls -l kern_event.o
-rw-r-----  1 sakae  wheel  120252 Nov 24 15:04 kern_event.o
vbox$ ctfstrip -S -o aa kern_event.o
vbox$ ls -l aa
-rw-r--r--  1 sakae  wheel  30696 Nov 24 15:48 aa
vbox$ strip kern_event.o
vbox$ ls -l kern_event.o
-rw-r-----  1 sakae  wheel  18772 Nov 24 15:49 kern_event.o

オリジナルのファイル容量は、120Kを越る巨大なやつ。そこからデバッグ情報だけを削除してaaってファイルを作成。そのサイズは30K。要するにgdbの為に水膨れしてた訳。 次に、普通のストリップを実施。わーい、更に小くなったぞ。

vbox$ nm kern_event.o
nm: kern_event.o: no name list
vbox$ nm aa
00000ab0 T KQREF
00000ad0 T KQRELE
 :

でもね、stripしちゃうと、本当に必要な名前まで削除されちゃうんだ。これでは使い物にならない。ctfstripで新に作ったものは大丈夫。ストリップは天下の大罪、下手にやると たいーほ されるぞ。

よって、こんなスクリプトをかませて、gdb情報だけを削除するのさ。

#!/bin/sh

for f in *.o
do
    name=`basename $f .o`
    ctfstrip -S -o ${name}.O $f
done

尚、basenameでサフィックスを削除出来るなんで、初めて知った。もぐりのスクリプターだなあ。まあ、こうやって野次馬根性を発揮するから、引出しの道具が増えるんだな。

色々な石でのファイルの出来具合を確認したい。共通なって事で、/sys/kern/kern*.c 相当だけを抜き出してみた。kern/ エリアのほぼ 1/3 をサンプリング調査って事になる。

結果はソースの行数でソートした、ベスト10です。実際は35ヶ有ります。

i386

  org .o        ctfstrip           strip          lines source
-----------------------------------------------------------------------
  168216           39976           27160            2540 kern_sysctl.c
  113552           25296           16528            2452 kern_sig.c
  120252           30696           18772            2200 kern_event.c
   96808           17492           12772            1643 kern_pledge.c
   96624           18212           11844            1474 kern_descrip.c
   63268           10832            6392            1129 kern_prot.c
   57308           14868            9464             989 kern_time.c
   53568           14756            9552             981 kern_tc.c
   63564           17992            9804             965 kern_timeout.c
   70184           12228            6884             916 kern_exec.c

amd64

290120           77328           35512            2540 kern_sysctl.c
197464           64624           29816            2452 kern_sig.c
222960           86144           38072            2200 kern_event.c
151536           45584           17472            1643 kern_pledge.c
168664           48688           20152            1474 kern_descrip.c
116008           34280           15664            1129 kern_prot.c
 93096           31288           13792             989 kern_time.c
 89384           31680           16896             981 kern_tc.c
110104           45464           17440             965 kern_timeout.c
105272           29552            8096             916 kern_exec.c

r64

781512          170552           25088            2540 kern_sysctl.c
486136          121216           16688            2452 kern_sig.c
562104          155096           20176            2200 kern_event.c
304216           59208           12008            1643 kern_pledge.c
391000           91960           13000            1474 kern_descrip.c
246272           56256            6320            1129 kern_prot.c
201872           54120            8392             989 kern_time.c
244144           71104            8568             981 kern_tc.c
226648           67944            9008             965 kern_timeout.c
216056           41824            6048             916 kern_exec.c

このままでは目がチラチラするので、gnuplotの散布図を作ってみます。

gnuplot> plot "i386" pointtype 6

また、わざわざ図にしなくても

gnuplot> stat "i386"
 :
  Minimum:         6152.0000 [33]     2021.0000 [32]
  Maximum:       168216.0000 [26]    39976.0000 [26]
  Quartile:       26508.0000          5468.0000
  Median:         53568.0000          9924.0000
  Quartile:       64088.0000         14168.0000

  Linear Model:       y = 0.2256 x - 994.7
  Slope:              0.2256 +- 0.01267
  Intercept:          -994.7 +- 805.1
  Correlation:        r = 0.9517

こんな風に、回帰曲線の推定やら相関係数まで表示してくれる。普通に使うならgnuplotで十分な気がするぞ。

今度はソースの行数(wcしただけなんでコメントも含まれる)と生成されたオブジェクト(striped)の突合せ。using x:y が自由に使えるので嬉しい。

gnuplot> stats 'i386' using 4:3
  :
  Linear Model:       y = 8.501 x + 98.51  ;; i386
  Linear Model:       y = 13.7 x + 663.1   ;; amd64
  Linear Model:       y = 8.49 x - 216.1   ;; r64

回帰式だけを拾ってみた。相関係数は 0.96ぐらいになってたんで、鉄板で予想がピタリと的中するというお墨付き。

amd64の場合は、他の石に比べて効率が悪い。大雑把に1000行のソースをコンパイルすると、13700バイトのオブジェクトファイルが出来ると予想。他の2つの石は8500バイトになるって予想。

syspatch

更に上のリストを見て気付いた事がある。*.oは、大体 Sep 28 03:41 から始まって Sep 28 03:54 に終了。カーネルのコンパイルに13分かかりましたって証拠が出てる。その日付以外に、仲間外れの Oct 27 02:41 ukbd.o なんてのが出てくる。これだけ仲間外れ。

更に後の日付のNov 24 05:41は、オイラーの指示によるものだ。

OpenBSD 7.2のリリース日は Oct 20 なんで、カーネルのコンパイルは随分前のSep 28に行われた。ukbd.o はリリース日以降なんて、パッチなんだな。そう、syspatchね。

vbox$ ls /var/syspatch/
72-001_x509/    72-002_asn1/    72-003_ukbd/    72-004_expat/   72-005_pixman/

ここの72-003以下を見ると 003ukbd.patch.sig

Apply by doing:
    signify -Vep /etc/signify/openbsd-72-base.pub -x 003_ukbd.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
    KK=`sysctl -n kern.osversion | cut -d# -f1`
    cd /usr/src/sys/arch/`machine`/compile/$KK
    make obj
    make config
    make
    make install

Index: sys/dev/usb/ukbd.c

こういうのが案内されてた。これで、更新されるんだね。make installで何が行われるかは、先程のrelinkの中にあるMakefileを見れば分る。

update-link:
        mkdir -p -m 700 /usr/share/relink/kernel
        rm -rf /usr/share/relink/kernel/GENERIC.MP /usr/share/relink/kernel.tgz
        mkdir /usr/share/relink/kernel/GENERIC.MP
        tar -chf - Makefile makegap.sh ld.script *.o | \
            tar -C /usr/share/relink/kernel/GENERIC.MP -xf -

newinstall:
        install -F -m 700 bsd /bsd && sha256 -h /var/db/kernel.SHA256 /bsd

install: update-link hardlink-obsd newinstall

また、パッチをロールバックするのに必要なtar玉も提供されてる。この玉は、syspatchした時に作られるとな。

vbox$ tar ztvf rollback.tgz
-rw-rw----  1 root     wheel        19048 Nov  4 14:08 usr/share/relink/kernel/GENERIC.MP/gap.o
-rw-r-----  1 root     wheel        30868 Sep 28 03:53 usr/share/relink/kernel/GENERIC.MP/ukbd.o
-rw-r-----  1 root     wheel         2112 Sep 28 03:54 usr/share/relink/kernel/GENERIC.MP/vers.o

/usr/share/relink 以下は、リリース以降の作業場所って地位が与えられている訳ね。なお、これらはinstall時やsysupgrade時に作成若しくは更新される。大事なファイル群なので、神聖につき侵すべからず、です。

memo

riscv64を起動する時の力持ちは、下記が参考になる。それってopensbiとの連携かな。

vbox$ emacs ./stand/efi/include/efiapi.h
vbox$ emacs ./stand/efi/include/efidef.h

This year's Index

Home