本記事はルーターハックAdvent Calendar 10日目の記事です。
9日目となる前回は、ルーターに生やした USB ポートに SD カードリーダーを挿し、rootfs として使えるようにしたうえで、sl をビルドするところまでを書いた。ゴールはルーター上での Python のビルドだったのだが、sl まででえらく時間がかかり気分的に記事を分けたくなったので前後編に分けてお送りする。
おことわり
技適 に関する筆者の配慮や考えについてはカレンダー1日目「技適とルーターハック」をご覧ください。本記事で紹介する内容は、法を遵守するための慎重な注意をもって書かれています。
]
やっと Python をビルドする
前回 sl をビルドするためにビルドした ncurses は Python のビルドにおいてもあったほうが嬉しいライブラリ(必須ではないはず?)なので結果オーライ。意を決して Python へ。
落とす
落としてextractするだけで2分かかっててたいへんさきがおもいやられますね(朗らかな笑顔)
root@OpenWrt:~# wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz Downloading 'https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz' Connecting to 151.101.0.223:443 Writing to 'Python-3.7.1.tgz' Python-3.7.1.tgz 100% |*******************************| 22267k 0:00:00 ETA Download completed (22802018 bytes) root@OpenWrt:~# time tar zxf Python-3.7.1.tgz real 2m 18.03s user 0m 19.11s sys 0m 8.08s
configure
configure。3分47秒経って死。
root@OpenWrt:~/Python-3.7.1# time ./configure ... checking for grep that handles long lines and -e... configure: error: no acceptable grep could be found in /usr/sbin:/usr/bin:/sbin:/bin:/usr/xpg4/bin Command exited with non-zero status 1 real 3m 46.97s user 0m 6.14s sys 0m 8.21s
フルなgrepを入れねば。ファームをビルドするときに busybox をモリモリにすれば解決しそうだが、それは一旦置いておく。
root@OpenWrt:~/Python-3.7.1# opkg install grep Installing grep (3.1-1) to root... Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/grep_3.1-1_mips_24kc.ipk Configuring grep.
configure。grep, ncurses通過の後成功。
root@OpenWrt:~/Python-3.7.1# time ./configure ... checking for grep that handles long lines and -e... /usr/bin/grep ... checking curses.h usability... yes checking curses.h presence... yes checking for curses.h... yes checking ncurses.h usability... yes checking ncurses.h presence... yes checking for ncurses.h... yes ... creating Makefile If you want a release build with all stable optimizations active (PGO, etc), please run ./configure --enable-optimizations real 1h 53m 48s user 5m 16.06s sys 4m 26.75s
あー --enable-optimizations
忘れてた。まあいっか。ともかく! 1時間53分で configure が成功を収めただけでも喜ぼう!
make
これから一体どれだけ時間がかかるのやら。思えばdistccでも使えばよかった。まあ使うとしたら後の機会に。
root@OpenWrt:~/Python-3.7.1# time make
……ここから外出し、約12時間後に見てみてもまだビルドが終わってなかった。
おそるおそる top を見る。
Mem: 26508K used, 1332K free, 20K shrd, 316K buff, 6284K cached CPU: 3% usr 11% sys 0% nic 0% idle 85% io 0% irq 0% sirq Load average: 2.02 2.04 2.02 1/41 25308 PID PPID USER STAT VSZ %VSZ %CPU COMMAND 25307 25306 root D 26144 94% 10% {cc1} /usr/lib/gcc/mips-openwrt-linux- 97 2 root IW 0 0% 4% [kworker/0:1] 406 2 root DW 0 0% 0% [usb-storage] 121 2 root SW 0 0% 0% [kswapd0] 25308 532 root R 1196 4% 0% top 7 2 root SW 0 0% 0% [ksoftirqd/0] 25306 24639 root S 3144 11% 0% gcc -c -Wno-unused-result -Wsign-compa 24639 24638 root S 2204 8% 0% make 784 1 root S 1700 6% 0% /sbin/netifd 1 0 root S 1544 6% 0% /sbin/procd 819 1 root S 1428 5% 0% /usr/sbin/odhcpd 1140 1 dnsmasq S 1324 5% 0% /usr/sbin/dnsmasq -C /var/etc/dnsmasq. 665 1 root S 1232 4% 0% /sbin/logd -S 64 532 1 root S 1204 4% 0% /bin/ash --login 15256 15255 root S 1204 4% 0% -ash 1457 1 root S< 1196 4% 0% /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd 15311 784 root S 1196 4% 0% udhcpc -p /var/run/udhcpc-eth1.pid -s 24638 15256 root S 1196 4% 0% time make 531 1 root S 1192 4% 0% /sbin/ubusd 15255 964 root S 1128 4% 0% /usr/sbin/dropbear -F -P /var/run/drop
VSZ 94%
これはまだマシで、ひどいときは 104% まで達していた。
ダメだ。完全にスラッシングしててまるでビルドが進んでない。VSZ 104% ってどんなだよ。物理メモリ飛び出してるやんけ。
— Takumi Sueda (@puhitaku) 2018年12月16日
比較として PC で Python をビルドすると、あともう5倍ぐらいの道のりは残されてそうだったので別のルーターに切り替えることにした。WHR-HP-GN よすまない、無理な仕事をさせてしまったな。
もっと性能の良いルーターで再チャレンジ
やはり敗因は、32 MiB と少なすぎる物理メモリと USB 1.1 という前時代の物理層しか搭載しないことだった。メモリがスワップした瞬間、Fallout シリーズの端末みたいなスピードでしか動作できなくなるし。
次なるルーターは 多摩電子工業 (Axing) W06 だ。
秋葉原はジャンク通りに面するショップインバースなどで見かけた人も多いだろう。一時期1000円を余裕で下回る価格で売られていた、持ち運びできるルーターだ。
- CPU: MediaTek MT7688AN@575MHz
- RAM: Winbond W9751G6KB@DDR2, 64MB
- Flash: 16MB
- Ether: FastEther (100Mbps) x1
- USB 2.0 PHY 搭載・物理ポート付き
(出典: 鉄PCブログ)
このルーターは価格に対してスペックが良い。CPU 575MHz。DDR2。USBポート付き。しかもUSB 2.0。WHR-HP-GN から四まわりほどアップグレードした感じだ。
こいつのファームをビルドし、ここまでとほぼ同じ作業をもう一度繰り返した。差分は以下。
- SDカードの rootfs パーティションを ext4 ではなく btrfs にした
Kernel Modules
->Filesystem
->kmod-fs-btrfs
どうあがいても USB の帯域幅は DRAM の帯域幅より狭い。btrfs は透過的に圧縮をかけることができるので、CPUが十分に高速であれば(いやそんなに速くないとは思うけど) USB の帯域を少しでも節約できるのではないかと考え選択した。kmod-fs-btrfs
を選択すれば、圧縮アルゴリズム系のモジュールも自動で選択してくれるようだ。
- これまでに不足していた ca-certificates や grep などを追加した
そもそも WHR-HP-GN のような Flash が 8 MiB のシステムでも ppp や Wi-Fi 関連の config を消せばいろいろ入った。こちらはFlash が 16 MiB もあるので余裕で追加できる。
準備
ファームをビルドして実機に転送して SD をフォーマットして extroot を仕込んで ncurses をビルドして Python を落としてくる。
blkid してみると。
root@OpenWrt:~# blkid /dev/mtdblock5: TYPE="squashfs" /dev/sda1: UUID="e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef" UUID_SUB="e8a7a945-1721-4fcf-99b4-669e49bea786" TYPE="btrfs" PARTUUID="3859753d-01" /dev/sda2: UUID="765a8ea4-ef55-48fe-95c7-8c0fee8e9c0e" TYPE="swap" PARTUUID="3859753d-02"
おー、ちっこいルーターなのに btrfs やら swap やら盛りだくさん!ウキウキな瞬間だ。
mount。
root@OpenWrt:~# mount -o ssd,noatime,discard,compress=lzo /dev/sda1 /mnt root@OpenWrt:~# dmesg | tail -n 11 [ 136.750060] BTRFS: device fsid e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef devid 1 transid 5 /dev/sda1 [ 136.771595] BTRFS info (device sda1): disk space caching is enabled [ 136.784117] BTRFS info (device sda1): has skinny extents [ 136.794659] BTRFS info (device sda1): flagging fs with big metadata feature [ 136.826883] BTRFS info (device sda1): checking UUID tree [ 168.921098] BTRFS info (device sda1): enabling ssd optimizations [ 168.933092] BTRFS info (device sda1): turning on discard [ 168.943652] BTRFS info (device sda1): setting 8 feature flag [ 168.954885] BTRFS info (device sda1): use lzo compression [ 168.965599] BTRFS info (device sda1): disk space caching is enabled [ 168.978028] BTRFS info (device sda1): has skinny extents
FOOO!!! さて、extrootしよう。マウントオプションの追加も忘れずに。
root@OpenWrt:~# cp -r /overlay/* /mnt/ root@OpenWrt:~# block detect > /etc/config/fstab root@OpenWrt:~# vi /etc/config/fstab root@OpenWrt:~# cat /etc/config/fstab config 'global' option anon_swap '1' option anon_mount '0' option auto_swap '1' option auto_mount '1' option delay_root '5' option check_fs '0' config 'mount' option target '/overlay' option options 'ssd,noatime,discard,compress=lzo' option uuid 'e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef' option enabled '1' config 'swap' option uuid '765a8ea4-ef55-48fe-95c7-8c0fee8e9c0e' option enabled '1' root@OpenWrt:~# reboot ... root@OpenWrt:~# df -h Filesystem Size Used Available Use% Mounted on /dev/root 9.0M 9.0M 0 100% /rom tmpfs 29.3M 52.0K 29.3M 0% /tmp /dev/sda1 13.7G 16.6M 11.7G 0% /overlay overlayfs:/overlay 13.7G 16.6M 11.7G 0% / tmpfs 512.0K 0 512.0K 0% /dev
成功!
opkg が相変わらず空気を読まない
GCC だけはファームに内蔵できないので opkg で入れる。
root@OpenWrt:~/ncurses-6.1# opkg update ... root@OpenWrt:~/ncurses-6.1# opkg install gcc
うーん時間がかかる。kworker が食ってるのはなんだ。
Mem: 51644K used, 8384K free, 0K shrd, 1240K buff, 2948K cached CPU: 1% usr 98% sys 0% nic 0% idle 0% io 0% irq 0% sirq Load average: 3.56 1.75 0.71 4/67 2100 PID PPID USER STAT VSZ %VSZ %CPU COMMAND 119 2 root IW 0 0% 51% [kworker/0:1] 2100 1911 root R 4368 7% 20% wget -q -O /tmp/opkg-olfGfA/binutils_2.27-1_mipsel_24kc.ipk http://downloads.openwrt.org/snapshots/packages/mipsel_24kc/base/binu
どうやら相変わらず opkg が tmpfs にパッケージを置こうとするせいでスラッシングしているらしい。tmp-dirを変える。
root@OpenWrt:~# mkdir tmp root@OpenWrt:~# opkg update ... root@OpenWrt:~# opkg install --tmp-dir /root/tmp gcc
うん、目に見えて速くなった。
ncurses リベンジ
落として configure 。
root@OpenWrt:~# wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz Downloading 'https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz' Connecting to 208.118.235.20:443 Writing to 'ncurses-6.1.tar.gz' ncurses-6.1.tar.gz 100% |*******************************| 3286k 0:00:00 ETA Download completed (3365395 bytes) root@OpenWrt:~# tar zxf ncurses-6.1.tar.gz root@OpenWrt:~# cd ncurses-6.1/ root@OpenWrt:~# time ./configure ... real 2m 13.33s user 1m 26.98s sys 0m 19.78s
かーなりマシになった。前は configure だけで40分かかっていたことを考えれば上出来だ。特にスラッシングも起きていないようだ。
make。
root@OpenWrt:~/ncurses-6.1# time make ... make[1]: Leaving directory '/root/ncurses-6.1/c++' real 30m 6.52s user 23m 35.15s sys 1m 32.85s
うーん速い!WHR-HP-GN が ncurses で configure するよりも速い。
Python リベンジ
落として configure。
root@OpenWrt:~# wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz ... root@OpenWrt:~# tar zxf Python-3.7.1.tgz root@OpenWrt:~# cd Python-3.7.1 root@OpenWrt:~# time ./configure ... real 4m 21.46s user 3m 29.45s sys 0m 42.52s
いい感じ。お次は makeなのだが、IO が発生するたびに kworker が相変わらず重い… gcc と CPU 取り合ってる…ということで btrfs の醍醐味 compress=lzo
はお蔵入りに。残念。
root@OpenWrt:~# time make
kworker は幾分か軽くなったのだが、大きいオブジェクトファイルをコンパイルする時はスワップが発生して途端に遅くなるようだ。とりあえず、開始2〜3時間で WHR-HP-GNが止まっていた部分は追い抜いた。
ここで寝落ちし朝を迎えた時点で typeobject.c
をコンパイルしていた。そのまま出勤。家に帰ると…
gcc -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -I. -I./Include -DPy_BUILD_CORE -o Objects/tupleobject.o Objects/tupleobject.c gcc -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -I. -I./Include -DPy_BUILD_CORE -o Objects/typeobject.o Objects/typeobject.c
いやおめー全然進んどらんが!
no-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -I. -I./Include -DPy_BUILD_CORE -o Objects/typeobject.o Objects/typeobject.c ^C^C^Cmake: *** [Makefile:1623: Objects/typeobject.o] Interrupt Command terminated by signal 2 real 17h 51m 36s user 11m 55.93s sys 5m 6.26s
途中まで進んで結局完全にスラッシングしちゃったのでした。フルな Python のビルドは無理でした。完。
Python 0.9.1 をコンパイルしてお茶を濁す
Python の開発の歴史は1980年代の末まで遡る。(突然始まる Python の歴史)もともとは Guido van Rossum のクリスマスの趣味プログラミングとして始まった Python は、1991年にバージョン 0.9.1 として世界に公開された。
実は、このバージョン 0.9.1 は今でもダウンロードし、コンパイルすることができる。
フルの Python はコンパイルできないので、こいつをコンパイルしてお茶を濁すことにする。
落としてコンパイルする。
root@OpenWrt:~# wget https://www.python.org/ftp/python/src/Python-0.9.1.tar.gz Downloading 'https://www.python.org/ftp/python/src/Python-0.9.1.tar.gz' Connecting to 151.101.0.223:443 Writing to 'Python-0.9.1.tar.gz' Python-0.9.1.tar.gz 100% |*******************************| 378k 0:00:00 ETA Download completed (387150 bytes) root@OpenWrt:~# tar zxf Python-0.9.1.tar.gz root@OpenWrt:~# cd python-0.9.1/src/ root@OpenWrt:~/python-0.9.1/src# CC=gcc time make ... real 0m 37.69s user 0m 33.26s sys 0m 2.67s
あっはっは、37秒ちょっとで終わってしまった。やはり時代の古いプログラムはコンパイルも軽い。最初からこうすればよかった感すらある。
Python 0.9.1 をちょっと触る
※現代の Python との主な違いについては、同梱されている README.reconstructed
にも書いてある。
print
はある。
>>> print 'hello world!' hello world!
dir()
の使用できる範囲が限られている。現在では任意の object の attribute を一覧にできるが、0.9.1 では module の中の関数を一覧化する程度の機能しかないようだ。
>>> i = 1 >>> dir(i) Unhandled exception: type error: dir() argument, must be module or absent Stack backtrace (innermost last): File "<stdin>", line 1 >>> >>> import math >>> dir(math) ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'e', 'exp', 'fabs', 'floor', 'log', 'log10', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
class の定義ではクラス名の次に必ずカッコを付ける必要がある。
>>> class Foo: Parsing error: file <stdin>, line 1: class Foo: ^ Unhandled exception: run-time error: syntax error
__init__
という概念がない。標準ライブラリではコンストラクタとしてクラスと同名の関数を作り、手で呼んでいたようだ。
実際、__init__
を作っても呼ばれないことがわかる。
>>> class Foo(): ... def __init__(self): ... print 'hello!' ... >>> f = Foo() >>>
ダブルクォートが文字列リテラルとして解釈されない。
>>> print 'Hello World!' Hello World! >>> print "Hello World!" Parsing error: file <stdin>, line 1: print "Hello World!" ^ Unhandled exception: run-time error: syntax error
次回
次回は、ルーターに備わっているペリフェラルにいろいろつないでみる企画『ルーターにつなぐ』の第1弾とする。まずは I2C にする予定だ。乞うご期待。