config
config
*BSDにはconfigが完備されている。リナ方面は、どうだったかすっかり忘れているけど。で、こやつは、並にあるconfigureとは違うからね。カーネル専用のツールだ。
機能は2つ有る。一つはカーネルの製作仕様書を元に、カーネルのコンパイル環境を構築するもの。もう一つは、現存のカーネルの機能(限定的なものだけど)を変更するものだ。今回は何時もお世話になる前者の機能を調べてみる。
try config
実験である。実験の為のconfigを ~sakae/configとして用意した。勿論gdb付きね。riscvな環境にも拘わらず、arch/i386をやろうと言う無謀な試み。
riscv# /home/sakae/config config: cannot read CONFIG: No such file or directory riscv# /home/sakae/config TESTME /mnt/src/sys/arch/i386/compile/TESTME/obj -> /usr/obj/sys/arch/i386/compile/TESTME config -b /mnt/src/sys/arch/i386/compile/TESTME/obj -s /mnt/src/sys /mnt/src/sys/arch/i386/conf/TESTME
まずは、ご挨拶がてら引数無しで起動。デフォでCONFIGと言う仕様書が使われるとな。で、そんなのが/sys/arch/i386/configの下にないので、怒られたとな。しょうがないので、GENERICな仕様書の名前をTESTMEでcpしたものを用意。
それで実行すると、リンクが作成された。そして、改めて詳細なオプション付きでconfigが実行された。config(8)の最初の形式だね。
config [-p] [-b builddir] [-s srcdir] [config-file]
この結果
riscv# pwd /usr/obj/sys/arch/i386/compile/TESTME riscv# ls Makefile drm.h isapnp.h pctr.h uhci.h :
後は、ここに移動してmakeするだけーーーー。世の中に有るconfigureと少しは似ているな。 強引にmakeすると
riscv# make cat /mnt/src/sys/arch/i386/i386/genassym.cf /mnt/src/sys/arch/i386/i386/genassym.cf | sh /mnt/src/sys/kern/genassym.sh cc -no-integrated-as -g -Werror -Wall -Wimplicit-function-declaration -Wno-pointer-sign -Wframe-larger-than=2047 -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -ffreestanding -fno-pie -mretpoline -O2 -pipe -nostdinc -I/mnt/src/sys -I/usr/obj/sys/arch/i386/compile/TESTME -I/mnt/src/sys/arch -I/mnt/src/sys/dev/pci/drm/include -I/mnt/src/sys/dev/pci/drm/include/uapi -I/mnt/src/sys/dev/pci/drm/i915 -DDDB -DDIAGNOSTIC -DKTRACE -DACCOUNTING -DKMEMSTATS -DPTRACE -DCRYPTO -DSYSVMSG -DSYSVSEM -DSYSVSHM -DUVM_SWAP_ENCRYPT -DFFS -DFFS2 -DFFS_SOFTUPDATES -DUFS_DIRHASH -DMFS -DUDF -DMSDOSFS -DFIFO -DFUSE -DSOCKET_SPLICE -DTCP_ECN -DTCP_SIGNATURE -DPPP_BSDCOMP -DPPP_DEFLATE -DPIPEX -DMROUTING -DMPLS -DBOOT_CONFIG -DUSER_PCICONF -DAPERTURE -DMTRR -DNTFS -DSUSPEND -DHIBERNATE -DPCIVERBOSE -DEISAVERBOSE -DUSBVERBOSE -DWSDISPLAY_COMPAT_USL -DWSDISPLAY_COMPAT_RAWKBD -DWSDISPLAY_DEFAULTSCREENS="6" -DX86EMU -DONEWIREVERBOSE -DMAXUSERS=39 -D_KERNEL -MD -MP -MF assym.P > assym.h.tmp cc: error: argument unused during compilation: '-mretpoline' [-Werror,-Wunused-command-line-argument] *** Error 1 in /usr/obj/sys/arch/i386/compile/TESTME (Makefile:1281 'assym.h')
riscvな環境ではi386をコンパイル出来無いと言う、当たりまえの結果となった。
出来上がったMakefileには、見所が沢山あるんで、後でじっくり見る事にして、取り敢えずの予告だけをば。
riscv# grep '.o:' Makefile | wc 1581 3169 60737
オブジェクトファイルとソースファイルの対応を数えてみた。ターゲットはこういうのね。
acpi_machdep.o: $S/arch/i386/i386/acpi_machdep.c acpi_wakecode.o: $S/arch/i386/i386/acpi_wakecode.S acpi_x86.o: $S/dev/acpi/acpi_x86.c
あちこちに点在(いや、kern,arch/i388,dev,netみたいにクラスターを作ってるな)してるソースをコンパイルして、集合させてるんだな。そしてそれを組み立てるとカーネルが出来上がる。
もう一つ、油断してたんだけど、*.hの中身を点検して、びっくらこいた。並のヘッダーファイルかと思ったら、それが大違い。じっくり見ると、発見があるな。いつもは、大急ぎで通り過ぎてますから。車でさっと行くんじゃなくて、ゆっくり散歩。あるいは、新幹線じゃなくて、各駅停車でゆっくりみたいな。。。
trace config
今度は舞台をi386なマシンに移して、そこでriscv用のconfigをやってみる。config仕様は、現役のriscvなOpenBSDを作るのに使ったやつ。
こんなgdbで初めてみた。まんまのオプションを与えている。
(gdb) b main Breakpoint 1 at 0xd07a: file main.c, line 141. (gdb) r -b /home/sakae/KERNEL -s /usr/src/sys SEEING Starting program: /home/sakae/config -b /home/sakae/KERNEL -s /usr/src/sys SEEING
歩兵が偵察を進めると、こんな所に到達。
/* * Ready to go. Build all the various files. */ => if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() || mkioconf()) stop(); optiondelta(); return (0);
一瞬、or で繋げてあるんで、おやって思ったんだけど、それぞれの関数が疑を返せば(0)、次の関数にトライする。最後のmkioconf()を実行しても疑なら、結局ifは失敗。ちょいと紛らわしいけど aa() && bb() && cc() みたいなのと一緒の事だ。
後は、これらの個別の関数を覗いてみるだけね。例えば、mkheaders()の冒頭部分
B for (fi = allfiles; fi != NULL; fi = fi->fi_next) { => if (fi->fi_flags & FI_HIDDEN) continue; if (fi->fi_flags & (FI_NEEDSCOUNT | FI_NEEDSFLAG) && emitcnt(fi->fi_optf)) return (1); }
ちょいと追ってみると、リンクになってた。アンチョコを見て、辿ってみる。
(gdb) p *fi $16 = {fi_next = 0x82152080, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\ fi_srcline = 82, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x8215a\ 1d0, fi_base = 0x8215a1f0 "smc93cx6", fi_optx = 0x82159360, fi_optf = 0x0, fi_m\ krule = 0x0} (gdb) p *$.fi_next $17 = {fi_next = 0x8217c6a0, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\ fi_srcline = 86, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x82154\ 7a0, fi_base = 0x82154ea0 "pcdisplay_subr", fi_optx = 0x82154e70, fi_optf = 0x0\ , fi_mkrule = 0x0} (gdb) $18 = {fi_next = 0x821524a0, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\ fi_srcline = 87, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x82159\ 860, fi_base = 0x82154660 "pcdisplay_chars", fi_optx = 0x8215ab20, fi_optf = 0x\ 0, fi_mkrule = 0x0}
from man
ここら辺でマニュアルを見ておくべきだろう。
option name Set a kernel option. Kernel options may take either the form NAME or the form NAME=value. These options are passed to the compiler with the -D flag. maxusers number Required. Used to size various system tables and maximum operating conditions in an approximate fashion. Multiple instances of this keyword may be specified. The number provided in the last instance will be used, and warnings will be printed for each duplicate value. This is convenient when used with the include directive. SEE ALSO options(4), bsd.re-config(5), files.conf(5), boot.conf(8), boot_config(8) The SYNOPSIS portion of each device in section 4 of the manual. S. J. Leffler and M. J. Karels, “Building 4.4 BSD Systems with Config”, 4.4BSD System Manager's Manual (SMM).
options(4)は大事だから見ておけ。カーネルの機能の説明がされている。それからfiles.conf(5)だな。こちらはデバイスがらみの説明。マシンのアーキテクチャ特有の設定が説明されてる。
この〜木なんの木、気になる木。ったら、どこかの会社のCMソングだったけど、カーネル屋さんも無視出来無い木。そう、デバイス・ツリーね。ああ、クリスマス・ツリーは、小学生の希望の木だ。先程散歩で、少学校の前を通ったら、生徒さんと先生が一生懸命に飾りつけしてた。
vbox$ cd /sys/arch/riscv64/conf/ vbox$ lv files.riscv64 # # mainbus # define mainbus {} device mainbus: fdt attach mainbus at root file arch/riscv64/dev/mainbus.c mainbus # # simplebus # device simplebus: fdt attach simplebus at fdt file arch/riscv64/dev/simplebus.c simplebus # Machine-independent FDT drivers include "dev/fdt/files.fdt"
mainbus(別名fdt)は、親がてっぺんのrootです。それは、mainbusって言う定義が有れば、mainbus.cを取込ますって言う意味の定義なんだな。こんな表現をdot図にしたら面白いかもしれないな。
また、こんな風にincludeも出来るんで、追跡してみる。
vbox$ lv /usr/src/sys/dev/fdt/files.fdt : # Google Goldfish real-time clock device gfrtc attach gfrtc at fdt file dev/fdt/gfrtc.c gfrtc
出てきましたねぇ。前回やったデバイスが。
これらが、Makefileを作る為の大本になるネタ帳だ(システムワイドな設定は、sys/confの下に有る)。
これらから種々選択して、おれ様カーネルを作る。その仕様ファイルが上の例だとSEEINGだ。GENERICは、これなら無事にインストール出来るでしょっと言う、最大規模っぽい仕様。
machine riscv64 include "../../../conf/GENERIC" : # mainbus mainbus0 at root # cpu0 cpu0 at mainbus0 intc0 at cpu0 virtio* at fdt? virtio* at pci? vio* at virtio? # Network syscon* at fdt? early 1 gfrtc* at fdt?
こんな風に、選りすぐっているのね。これを減らせば、より小さいカーネルが作成出来る。今度、最小のカーネル作りに挑戦してみるかな。
configの成果物
出来上がったカーネル作成環境を見ておく。
ob$ tail -3 KERNEL/Makefile .PHONY: config config: cd /home/sakae && config -s /sys -b /home/sakae/KERNEL SEEING
普通のconfigureでも、ログにこれ相当が有ったような気がするぞ。KERNELってdirは、存在しなかったら、勝手に作成されるんで、ファイルが散らかる事は無い。
Makefileが作られるのは当然の事。machineってリンクが作成されてた。マシン依存なincludeエリアへのリンク。よくやる手だな。
ヘッダーファイルが93ヶ作成されてた。いずれも行数が数行のもの。
ob$ wc *.h | sort -nr | head 130 390 2282 total 11 33 232 rasops_glue.h 7 21 136 lpt.h 5 15 94 com.h 5 15 94 ahci.h 3 9 71 wsfont_glue.h 3 9 57 sti.h 3 9 53 vga.h 3 9 50 cy.h 3 9 48 uhid.h ob$ cat com.h #define NCOM 1 #define NCOM 1 #define NCOM_CARDBUS 0 #define NCOM_GSC 0 #define NCOM_ISAPNP 0
代表的なcom.hをみたけど、#defineだけだった。他も同様だ。どういう基準でこういうのを作っているのだろう? このdefineが/sysの中のソース中に存在するかと思うと、そうでもない奴が有ったりするぞ。
Makefile
大事なレシピなんで、少し大事に見てく。 冒頭を飾るのは、-Dの定義類。仕様書のopeionが、これに変換される。機能を有効にするか否かの設定。
ob$ head -1 Makefile | tr ' ' '\n' IDENT=-DDDB -DDIAGNOSTIC -DKTRACE -DACCOUNTING : -DWSDISPLAY_COMPAT_RAWKBD -DWSDISPLAY_DEFAULTSCREENS="6"
ソース中で、#ifdef KTRACE みたいな形で使われ、ソースを有効/無効にしてる。options(4)に個別の説明が有る。強引にtrで展開しちゃったけど、optionsってファイルが出来ているんで、それを見るのがスマートだ。
次は、PARAM=-DMAXUSERS=80 だ。かの昔、FreeBSDで独自カーネルを作る時、これを小くしたらいいだろうって事でやってみた事がある。どう反映されるの? Makefile中では、
INCLUDES= -nostdinc -I$S -I${.OBJDIR} -I$S/arch \ -I$S/dev/pci/drm/include \ -I$S/dev/pci/drm/include/uapi CPPFLAGS= ${INCLUDES} ${IDENT} ${PARAM} -D_KERNEL -D__${_mach}__ -MD -MP
こんな風に使われている。CPPって事は、ヘッダーがらみなのかな? 取り敢えず、ガサ入れってか家捜し。
ob$ grep MAXUSERS -rI . ./conf/param.c: * Compiled with -DHZ=xx -DMAXUSERS=xx ./conf/param.c:#define NPROCESS (30 + 16 * MAXUSERS) ./conf/param.c:int maxfiles = 5 * (NPROCESS + MAXUSERS) + 80;
ふーん、お一人様平均でプロセスは16ヶぐらいは使うでしょう。ファイル数は、それを元に計算しますとな。その他のパラメータも、これらを元に算出してる。これで、長年の疑問が氷解したな。
参考までにparam.cを見ると
int maxprocess = NPROCESS; int maxthread = 2 * NPROCESS; int maxfiles = 5 * (NPROCESS + MAXUSERS) + 80;
こんな、関数の外側で定義されてる変数が出て来る。これって、いわゆるカーネル変数じゃん。運が好ければ、後で変更出来るはず。ちょいと確かめてみる。
vbox$ sysctl -a | grep maxprocess vbox$ sysctl -a | grep utc_offset kern.utc_offset=540 vbox$ sysctl -a | grep maxthread kern.maxthread=2620 vbox$ sysctl -a | grep maxfiles kern.maxfiles=7030
勝手に変更すると痛い目に遭いそうなものも混ってますが。。。
trace open
configがどんなファイルをオープンするのだろう? ktraceは実ファイル名が出てこないので除外。そうするとgdbなスクリプトだなあ。こんなスクリプトを用意した。
b open commands silent bt 1 cont end run -b /tmp/KERNEL -s /usr/src/sys SEEING quit
何度もトライするので、/tmpな環境です。起動は、
vbox$ gdb -q /home/sakae/config -x script >LOG
gdbレディーなconfigが/home/sakae/configとして用意してあるので、それを利用。結果はat 行が不要なのでオミットする。
vbox$ cat LOG | sed '/at /d' | lv #0 _libc_open_cancel ( path=0x43e5a680 "/usr/src/sys/arch/riscv64/conf/files.riscv64", flags=0) #0 _libc_open_cancel (path=0x43e55340 "/dev/null", flags=0) #0 _libc_open_cancel (path=0x43e7a680 "/usr/src/sys/conf/files", flags=0) #0 _libc_open_cancel (path=0x43e7ab00 "/usr/src/sys/dev/pv/files.pv", flags=0) #0 _libc_open_cancel (path=0x43e49400 "/usr/src/sys/dev/hid/files.hid", : #0 _libc_open_cancel (path=0x6e1b7d00 "/usr/src/sys/dev/usb/files.usb", #0 _libc_open_cancel (path=0x6e1a8280 "/usr/src/sys/conf/GENERIC", flags=0) #0 _libc_open_cancel ( path=0x4f704e00 "/usr/src/sys/arch/riscv64/conf/Makefile.riscv64", flags=0) #0 _libc_open_cancel (path=0x15d6b45b "Makefile", flags=1537) #0 _libc_open_cancel (path=0xcf7db1ac "drm.h", flags=0) #0 _libc_open_cancel (path=0xcf7db1ac "vga.h", flags=0) : #0 _libc_open_cancel (path=0xcf7db1ac "ulpt.h", flags=0) #0 _libc_open_cancel (path=0xcf7db1ac "athn_usb.h", flags=0) #0 _libc_open_cancel (path=0x15d6c629 "ioconf.c", flags=1537) #0 _libc_open_cancel (path=0xcf7db938 "ioconf.incl.riscv64", flags=0) #0 _libc_open_cancel (path=0x15d6aa44 "options", flags=0) #0 _libc_open_cancel (path=0x15d6aa44 "options", flags=1538)
最小のカーネル
次は、最小のカーネルをどうやって作るかだ。それには、現状のデバイスツリーの探索結果が格納されているgmesgを調べる必要が有るな。
dmesg
現在のriscvのdmesg
mainbus0 at root: riscv-virtio,qemu cpu0 at mainbus0: vendor 0 arch 70100 imp 70100 rv64imafdch_zicsr_zifencei_zba_zLa\M-< %\M-c[\M^V\M-$#\M-Z\^B\M-@\M^?\M^?\M^? intc0 at cpu0 "fw-cfg" at mainbus0 not configured "flash" at mainbus0 not configured simplebus0 at mainbus0: "platform" simplebus1 at mainbus0: "soc" syscon0 at simplebus1: "poweroff" syscon1 at simplebus1: "reboot" syscon2 at simplebus1: "test" plic0 at simplebus1 gfrtc0 at simplebus1 com0 at simplebus1: ns16550, no working fifo com0: console pciecam0 at simplebus1 pci0 at pciecam0 "Red Hat Host" rev 0x00 at pci0 dev 0 function 0 not configured virtio0 at simplebus1: Virtio Block Device vioblk0 at virtio0 scsibus0 at vioblk0: 1 targets sd0 at scsibus0 targ 0 lun 0: <VirtIO, Block Device, > sd0: 4096MB, 512 bytes/sector, 8388608 sectors virtio1 at simplebus1: Virtio Block Device vioblk1 at virtio1 scsibus1 at vioblk1: 1 targets sd1 at scsibus1 targ 0 lun 0: <VirtIO, Block Device, > sd1: 2048MB, 512 bytes/sector, 4194304 sectors virtio2 at simplebus1: Virtio Network Device vio0 at virtio2: address 52:54:00:12:34:56 virtio3 at simplebus1: Virtio Unknown (0) Device : virtio7 at simplebus1: Virtio Unknown (0) Device "clint" at simplebus1 not configured vscsi0 at root scsibus2 at vscsi0: 256 targets softraid0 at root scsibus3 at softraid0: 256 targets
関係しそうな物を抜き出してみた。
dmassage
昔fuguitaで見付たawk版のもの、 dmassage
portsにも有ったんで有名なのか(FreeBSDには無かったぞ)、perl版数のやつ。perl版数で試す。dmesgのログは上の奴をriscDMってした。
vbox$ dmassage -d riscvDM -t root |-mainbus0 | |-cpu0 | | \-intc0 | |-simplebus0 | \-simplebus1 | |-com0 | |-gfrtc0 | |-pciecam0 | | \-pci0 | |-plic0 | |-syscon0 | |-syscon1 | |-syscon2 | |-virtio0 | | \-vioblk0 | | \-scsibus0 | | \-sd0 | |-virtio1 | | \-vioblk1 | | \-scsibus1 | | \-sd1 | |-virtio2 | | \-vio0 | |-virtio3 | \-virtio7 |-softraid0 | \-scsibus3 \-vscsi0 \-scsibus2
かっちょいい!!
riscv$ pkg_info dmassage Information for inst:dmassage-0.6p4 Comment: dmesg parser Description: dmassage parses your system's dmesg to learn which devices are successfully detected by the kernel. This information can be used for three purposes: to make the kernel boot faster, to help build a smaller kernel or to show all the devices in a tree-like hierarchy. Maintainer: The OpenBSD ports mailing-list <ports@openbsd.org>