Easy-ISLisp in Many OS
9年は使っている我が家のTVとリモコン。そのリモコンの調子が悪い。寒い朝、リモコンから赤外線が発射できない模様。それゆえ、電源がonにならないのだ。TVの近くでリモコンを操作すると、やっと電源が入る。でも、今度はチャンネルの切り替えが出来なかったりする。
女房大弱り。TV本体には、USB-Diskの操作機能が付いていないので、どうしたものかね? 電池のへたりを疑って、新品にしてみたけど関係なかった。
きっと、寒さで言う事をきかないだろうってんで、炬燵で温めてみた。そしたら、普通に動いたぞ。LEDでも劣化しかかっているのだろうか?
寒くなると、テレビのリモコンが効きません。リモコンをあっためる以外にいい方法はないでしょうか。
へー、そうだったのか。ゴムの劣化ね。
テレビ用の故障したリモコンをダメもとで分解してみた。簡単な原因で修理できた!!
やってみるか。
OpenBSD で、再びEasy-ISLisp
前にやって、コンパイルエラーで投げ出していたののリベンジ。
ちらちらとソースを見ていたら、こんなのに出くわした。
#if __linux #include <signal.h> #endif #if _WIN32 #include <windows.h> #endif
linuxとWindowsで、ソースを切り替えてるじゃん。しかも、リナちゃん限定。これじゃ、BSDで失敗するの当たり前じゃん。自分のふ甲斐無さに呆れておこう。 これが分れば、何とかなるな。
BSD系にするには、__OpenBSD__ とかを使え。いや、もっとおおらかにできないかね?
C言語 マクロ コンパイル 判定 OS コンパイラ を参考に、unixで一括りにしちゃえ。この場合、リナはunixではありませんと、とんがった事を主張されるかも知れないけどね。
何と言ってもGNUとLinuxは蜜月状態。GNUを起こした教祖様は、"GNU's Not Unix!"と言う再帰的な定義を最初にしていますからねぇ。ゆえに、GNUの息がかかったgccでは、きっとリナとunixは分離されてるはず。>誰か確かめておくれよ。
教祖様は、ブランド物のunixを嫌ったと言うか、ソースを見るなら金払え、口外しちゃならん、改変しちゃならんって閉鎖的態度に業を煮やして、GNUを立ち上げてる。 See GNU
この意義を思うに、私作る人、私食べる人とばかり、役割分担が進んでしまった文化は、何か味気ない気分になるぞ。>りなちゃん。まあ、タコを大事にしようを声を大に叫んでいた人達の努力が実ったと言うと、その通りなんですけどね。
ここ数年、MSもWSLなんて言うリナを出すご時世ですから、あなどりがたしですね。でも、WSLは、遅いのでガンガンとコンパイル(と、DISKを大量に読み書き)する人には、お勧め出来ない、実感がある。あくまで、セカンドオピニオンで使うべし。
また、爺の戯言かよ、先に行きましょ。 ソースのあちこちに散らばっているので、下記で一気に変換。ついでに、#ifdef に修正しておいた。
ob64$ for f in *.[ch] > do > sed -e 's/#if __linux/#ifdef __unix__/' < $f >$$ > mv $$ $f > done
ob64$ make gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o -o eisl -lm -ldl /usr/bin/ld: cannot find -ldl collect2: ld returned 1 exit status *** Error 1 in /home/sakae/eisl-port (makefile:2 'eisl')
どうも、dlってライブラリィーが見つからないみたい。これ悪しき習慣で、正式名称は、libdl* って名前のはず。そんなの眼を皿のようにして探してみても無い。
これが使われるのは、dlopenとかなんで、manしてみると、しっかり出て来る。但し、惜しい事に、どんなライブラリィーを使えとは出てこない。
こういう時は、お友達のNetBSDを調べるのが鉄則。FreeBSDは、どちらかと言うと、リナに近くて参考にならない。そう言えば、前回rubyを作った時に、ちゃんとdlライブラリィーの存在を確認したね。
DLFCN(3) Library Functions Manual DLFCN(3) NAME dlopen, dlclose, dlsym, dlvsym, dladdr, dlctl, dlerror - dynamic link interface LIBRARY (These functions are not in a library. They are included in every dynamically linked program automatically.) SYNOPSIS #include <dlfcn.h> void * dlopen(const char *path, int mode);
なんと、自動で導入してくれるんで、宣言は必要ないとな。全く、こういうの開発者の都合が優先されてて、知らない馬鹿は手を出すなと言われてるみたい。
そんな訳で、makefileを少し変形した。ついでにgdb観光出来るようにもしておいた。
ob64$ cat makefile eisl : main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o -o eisl -lm main.o : main.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c main.c function.o : function.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c function.c data.o : data.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c data.c gbc.o : gbc.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c gbc.c cell.o : cell.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c cell.c syntax.o : syntax.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c syntax.c bignum.o : bignum.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c bignum.c compute.o : compute.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c compute.c error.o : error.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c error.c extension.o : extension.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c extension.c edit.o : edit.c eisl.h gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c edit.c .PHONY: clean clean: rm -f eisl *.o
これで、コンパイルしてみる。
ob64$ make gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c main.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c function.c function.c: In function 'f_read_byte': function.c:1765: warning: comparison is always false due to limited range of data type function.c:1778: warning: comparison is always false due to limited range of data type gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c data.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c gbc.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c cell.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c syntax.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c bignum.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c compute.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c error.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c extension.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall -c edit.c gcc -ggdb3 -O0 -Wno-pointer-to-int-cast -Wall main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o -o eisl -lm extension.o: In function `f_random_real': /home/sakae/eisl-port/extension.c:266: warning: rand() may return deterministic values, is that what you want? main.o: In function `print': /home/sakae/eisl-port/main.c:1078: warning: strcat() is almost always misused, please use strlcat() /home/sakae/eisl-port/main.c:1077: warning: sprintf() is often misused, please use snprintf() main.o: In function `flttoken': /home/sakae/eisl-port/main.c:745: warning: strcpy() is almost always misused, please use strlcpy()
OpenBSDの親分さんからのセキュリティ勧告が出て来た。これは、まあ無視してもいい。いや、冷酒と親の小言は、後で利くから、修正しておくのが吉かな。
startup.lsp
ソースを見てたら、replに飛び込む前にstartup.lspが存在すると、それを実行してから会話が始まるようになってた。いいものを見つけたな。
これdebugと称する観光にうってつけ。emacsからgdbを起動してアプリからの会話が始まると、切り替えが面倒になるんだ。
だから、観光用にちょいと修正しとく。 main.c
251 int main(int argc, char *argv[]){ 252 int opt; 253 254 // printf("Easy-ISLisp Ver0.91\n"); 255 initcell(); 256 initclass();
こんな風にして、emacsにセッション用画面を出さない。そのかわり挙動を観察したいlispコードを、startup.lspに書いておく。
ob64$ cat startup.lsp (cons 6342 4649) (cdr '(111 222 333)) (quit) (print "Easy-ISLisp Ver0.91.ported")
(quit)をコメントにすれば、普通にバナーが出て来る仕様にしといた。
数字は、1とか2を使うと何だか分からなくなるので、ユニークなものが良い。たまたま、宮本武蔵を読んでいたんで、むさしによろしくにした。普通は、16進表記で、deadbeef とかにするのが、定番だけどね。
(gdb) b f_cons Breakpoint 1 at 0x9fbe: file function.c, line 1183. (gdb) r Starting program: /home/sakae/eisl-port/eisl Breakpoint 1, f_cons (arglist=1834) at function.c:1183 1183 arg1 = car(arglist); (gdb) bt #0 f_cons (arglist=1834) at function.c:1183 #1 0x1a67bedb in apply (func=1062, args=1834) at main.c:1447 #2 0x1a67bc63 in eval (addr=1832) at main.c:1416 #3 0x1a6841c1 in f_load (arglist=1826) at function.c:1971 #4 0x1a6788e7 in main (argc=1, argv=0xcf7c0e74) at main.c:280
BPを置くのは、f_cons。手抜きしてconsに置くと、変な所に連れ込まれて脱出に苦労する。関数の頭にfが付いているのが、ユーザーに公開されてるものだ。
アリティーによって登録場所を分けているかと思ったら、そうでもないのね。
int f_cons(int arglist){ int arg1,arg2; B => arg1 = car(arglist); arg2 = cadr(arglist); if(length(arglist) != 2) error(WRONG_ARGS, "cons", arglist); return(cons(arg1,arg2)); }
少し進めて、ご本尊様の作り方をみておく。核心だからね。ISLispって今風にクラスがあるのね。これで、表現を変えれば、minirubyになるかも。
int cons(int car, int cdr){ int addr; addr = freshcell(); SET_TAG(addr,LIS); SET_CAR(addr,car); SET_CDR(addr,cdr); SET_AUX(addr,ccons); //cons class => return(addr); }
ここに出て来たSET_CARとかって、C語のマクロと思われるけど、ソースを調べないと分からないの? いいえ、コンパイルした時に与えたオプション -ggdb3 が、マクロ定義まで取り込んでくれています。使い方は、下記のように簡単です。御利益は他にもあって、macro expand XX すると、マクロ XX を展開して見せてくれます。lisperには、お馴染みですね。
(gdb) info macro SET_CAR Defined at /home/sakae/eisl-port/eisl.h:112 included at /home/sakae/eisl-port/function.c:16 #define SET_CAR(addr, x) heap[addr].val.car.intnum = x
どの様に格納されたか、ちょいと確認
(gdb) p heap[addr] $2 = { val = { fltnum = 9.0315200059779868e-321, lngnum = 1828, { car = { intnum = 1828, subr = 0x724, port = 0x724, dyna_vec = 0x724 }, cdr = { intnum = 1829 } } }, aux = 21, prop = 0, tag = 10 '\n', flag = FRE, name = 0x0, option = 0 '\000', trace = 0 '\000' } (gdb) p heap[1828] $5 = { val = { fltnum = 3.1333643259251856e-320, lngnum = 6342, { car = { intnum = 6342, :
ちゃんと入っているな。所でcell構造体が随分と巨大っぽいぞ。
(gdb) p (sizeof (cell)) $6 = 48
これ、64bit環境でのサイズ。32Bit環境では、32Byteでした。
at NetBSD
気をよくしたオイラーは、NetBSDでも試してみる事にした。無事にコンパイル成功。起動すると 、
nb8$ ./eisl ksh: ./eisl: Cannot allocate memory
哀れな事に、しょっぱなから未知との遭遇。メモリーを確保できないエラーが発生。 これは、リミッターに引っかかっているんだな。一体どれぐらい欲しいんや?
同時に起動してた32Bit機で確認。NetBSDは、64Bit機なんで、その環境で調べるのが正しいけど、目安って事で。
debian:eisl$ ps awxl | egrep '(RSS|eisl)' F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 0 1000 9644 7307 20 0 320396 313896 wait_w S+ pts/2 0:00 ./eisl 0 1000 9666 8048 20 0 5128 820 pipe_w S+ pts/3 0:00 grep -E (RSS|eisl)
nb8$ ulimit -a time(cpu-seconds) unlimited file(blocks) unlimited coredump(blocks) unlimited data(kbytes) 262144 stack(kbytes) 4096 lockedmem(kbytes) 670282 memory(kbytes) 2010848 nofiles(descriptors) 1024 processes 1024 threads 1024 vmemory(kbytes) unlimited sbsize(bytes) unlimited
data部分が不足してるぞ。増やそう。man ksh して、ulimitを検索するんだな。(ulimitはshellの内蔵サービスコマンドのため)
nb8$ ulimit -d 500000 nb8$ ulimit -d 500000 nb8$ ./eisl Easy-ISLisp Ver0.91 >
目分量で指定しちゃったけど、実際はどれぐらい使ってるか、確認しとく。
nb8$ ps awxl | egrep '(eisl|RSS)' UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 1000 908 50 0 85 0 483832 470900 ttyraw I+ pts/1 0:00.34 ./eisl 1000 400 1030 0 85 0 7884 456 pipe_rd O+ pts/2 0:00.00 egrep (eisl|RSS) (ksh)
なかなか、良い勘してたな。
そう言えば、友人が大人の工場見学で、日産の栃木に行ってきたとか。逸品物の手作りをやってる所。公道では、スピードリミッターが働き、180Kmらしいけど、380Kmだせる何とかGTRもやってたそうな。そんなの走る所あるんか?
それは有る。サーキット乘ね。GPSでサーキット場を検出すると、自動的にスピードリミッターが解除されるらしい。車の方が進歩してんじゃん。
nb8$ readelf -l eisl : Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align : LOAD 0x0000000000042000 0x0000000000642000 0x0000000000642000 0x0000000000002bec 0x000000001ce9af98 RW 200000 : Section to Segment mapping: Segment Sections... : 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss :
こんなのを見て、ああメモリー沢山使うんだなあと感嘆しましょ。でも、惜しいかな、MemSizの表示は、16進数だ。それって、どんだけ?
そんなの、emacs付属のcalcに任せちゃえ。
Calc: 16#1CE9AF98
こんな風に、16進だよ、おっかさんと言いつつ入力する。とても大きな10進数値に変換された。桁を数えるの大変。1e6で割り算して、メガ単位にしちゃえ。答は、485.076888と出てきた。
Radix Mode (in Info calc)
The key sequences `d 2', `d 8', `d 6', and `d 0' select binary, octal, hexadecimal, and decimal as the current display radix, respectively. Numbers can always be entered in any radix, though the current radix is used as a default if you press `#' without any initial digits. A number entered without a `#' is _always_ interpreted as decimal. To set the radix generally, use `d r' (`calc-radix') and enter an integer from 2 to 36. You can specify the radix as a numeric prefix argument; otherwise you will be prompted for it.
2-36進数まで、任意に表示設定もできるのね。
アプリがどれぐらいメモリーを欲しがってるか分かっているんだから、自動設定せいや。そんなの即却下します。 それを許せば、やりたい放題になってしまうからね。せめて、自己規制しとけってのが、unixの流儀なのさ。
main.c
//heap and stack cell heap[HEAPSIZE]; int stack[STACKSIZE]; int argstk[STACKSIZE]; int cell_hash_table[HASHTBSIZE]; int shelter[STACKSIZE];
メモリーの使い方は、こんな風になってた。いずれも、bss領域、ulimitで言う、data領域になるんだな。(bss領域は、C語のソース上でエリアを確保した領域。data領域は、値が代入されてる変数とか、文字定数を格納するエリアになる。bss領域は、アプリ起動時にzeroクリアされる事になってる)
eisl.h
#define HEAPSIZE 10000000 // (10 * 1000 * 1000) #define STACKSIZE 300000 // (300 * 1000)
0が多数続く定義は、年寄には辛い。一個余分に数えたり、足りなかったり。OpenBSDの開発者達も同様と見えて、コメントに書いた定義方法を推奨してる。年寄の知恵だなあ。
マクロは、カッコとコッカでガードする事を忘れるな。忘れると痛い目にあうぞ。
カッコ、コッカって、常識と思っていたら、 括弧の始まりを「カッコ」、括弧の終わりを「コッカ」と呼ぶ?な記事が投稿されてわい。
at FreeBSD
FreeBSD 12.0でも試してみる。
$ cc -v FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1) Target: x86_64-unknown-freebsd12.0 Thread model: posix InstalledDir: /usr/bin
コンパイラーは、GNUの息のかかっていない物。そのかわり、あぷるの支援を受けて開発されている(いた)はず。なんたって、あぷるしか使っていなかった、オブジェクトCから早く脱却したくて、支援してたはず。swiftに軸足を移した今は、どうなんだろう?
$ make cc -O3 -Wno-pointer-to-int-cast -Wall -c main.c main.c:87:18: warning: implicit conversion from enumeration type 'toktype' to different enumeration type 'backtrack' [-Wenum-conversion] token stok = {GO,OTHER}; ~ ^~~~~ 1 warning generated. cc -O3 -Wno-pointer-to-int-cast -Wall -c function.c function.c:1765:16: warning: result of comparison of constant -1 with expression of type 'unsigned char' is always false [-Wtautological-constant-out-of-range-compare] if(res == EOF){ ~~~ ^ ~~~ : cc -O3 -Wno-pointer-to-int-cast -Wall -c bignum.c bignum.c:977:13: warning: absolute value function 'abs' given an argument of type 'long long' but has parameter of type 'int' which may cause truncation of value [-Wabsolute-value] j = abs(j); ^ bignum.c:977:13: note: use function 'llabs' instead j = abs(j); ^~~ llabs : cc -O3 -Wno-pointer-to-int-cast -Wall main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o -o eisl -lm -ldl
警告が親切だな。-ldl が、リナと同様に必要になってるよ。manによると必要無いって書いてある。
$ ldd eisl eisl: libm.so.5 => /lib/libm.so.5 (0x800280000) libdl.so.1 => /usr/lib/libdl.so.1 (0x8002b2000) libc.so.7 => /lib/libc.so.7 (0x8002b6000)
書けばリンクされるし、書かねばリンクされない、二刀流だな。
$ ldd eisl eisl: libm.so.5 => /lib/libm.so.5 (0x800280000) libc.so.7 => /lib/libc.so.7 (0x8002b2000)
compiler.lspのgccをccに変更してあげたら、
> (compile-file "tarai.lsp") > (load "tarai.o") T > (tarai 10 5 0) 10
動いちゃったよ。貪欲にgccの機能を取り入れてるって事かな。 まあ、目出度い事だな。
撲滅 Linux
こんな事を言うとLinux陣営から、石つぶて、手裏剣、毒矢、ロケット砲とかが飛んできそう。 それを言うなら、マサカリが飛んでくるって言う、内輪用語があるだろに。最近余り聞かないけどね。
えと、釈明会見を開いて、誤解を払拭したいと思います。
上で、ccを使って、tarai.lspのコンパイルと実行が成功しちゃった。けど、案内に invoke GCCて出て来て、かっこわるいなと、再び、compiler.lspを見ていたんだ。 そしたら、気になる部分を発見。
(let ((option (cond ((eq (self-introduction) 'windows) "cc -O3 -shared -o ") ((eq (self-introduction) 'linux) "cc -O3 -w -shared -fPIC -o ")))
linuxなんて決め打ちされてる。C語のCPPで言うと、#ifdef __linux 相当。 self-introduction関数で、埋め込まれた環境文字(この場合は、シンボルだな)を得て、それがリナかウィンドウか判別。今は、FreeBSDなんだから、対応したシンボルを出力してよね。
$ ./eisl Easy-ISLisp Ver0.91 > (self-introduction) LINUX
DNAをそのまま受け継いでいるので、性格にも表れている。かくして、撲滅LINUXである。
extension.c
#ifdef __FreeBSD__ int f_self_introduction(int arglist){ return(makesym("LINUX")); } #endif #if _WIN32 int f_self_introduction(int arglist){ return(makesym("WINDOWS")); } #endif
まあ、実害が無いから、そのままにしておこうかね。まだ、死にたくない。
at jail
前回やった、FreeBSD 12.0 上のjail。塀の中は、FreeBSD 9.3の環境だった。あの時は、詳細に使う事はしなかったけど、今回はコンパイルに挑戦してみる。
おっと、その前に一つだけ管理者としてやっておく事がある。adduserでアカウントとそのhomeが作られいる。けど、homeがrootの持ち物になってて、居住者が物(ファイル)置けないんだ。chownして準備しよう。それから、気になったら、chmodもしておこう。リナと違って、よそ様を覗ける扱いになってる。これ、刑務所仕様じゃないな。
カーネルに後方互換性が有ると言うけど、本当か確かめる意味も込めてね。デフォでgccが入っていた。gccからclangに切り変わるタイミングと思われるので、放置されたっぽい古いやつだ。
g13@jail9:~ % gcc -v Using built-in specs. Target: amd64-undermydesk-freebsd Configured with: FreeBSD/amd64 system compiler Thread model: posix gcc version 4.2.1 20070831 patched [FreeBSD]
g13@jail9:~/eisl-master % make gcc -O3 -Wno-pointer-to-int-cast -Wall -c main.c -lm gcc: -lm: linker input file unused because linking not done gcc -O3 -Wno-pointer-to-int-cast -Wall -c function.c -lm -ldl function.c: In function 'f_read_byte': function.c:1765: warning: comparison is always false due to limited range of data type function.c:1778: warning: comparison is always false due to limited range of data type gcc: -lm: linker input file unused because linking not done : gcc -O3 -Wno-pointer-to-int-cast -Wall main.o function.o data.o gbc.o cell.o syntax.o bignum.o compute.o error.o extension.o edit.o -o eisl -lm -ldl /usr/bin/ld: cannot find -ldl *** [eisl] Error code 1 Stop in /usr/home/g13/eisl-master.
多少の警告の後、-ldlが無いエラー。OpenBSDスタイルかな?
DLOPEN(3) FreeBSD Library Functions Manual DLOPEN(3) NAME dlopen, fdlopen, dlsym, dlfunc, dlerror, dlclose -- programmatic inter- face to the dynamic linker LIBRARY Standard C Library (libc, -lc)
libcに同梱してるとな。リンクの指示を外したら、コンパイル成功。無事に動いた。 カーネルの互換性はありそうだね。
$ ps awxl UID PID PPID CPU PRI NI VSZ RSS MWCHAN STAT TT TIME COMMAND : 0 7319 995 0 20 0 12824 3924 select I 1 0:00.00 sudo jexec -U g13 j9 /bin/tcsh 10001 7320 7319 0 20 0 14516 3160 pause IJ 1 0:00.08 /bin/tcsh 10001 7663 7320 0 52 0 487280 471680 ttyin I+J 1 0:03.42 ./eisl
eislが監獄の中で動いている時、親側からモニターしてみた。statの欄に、Jが付いていて、監獄内って事が分る。 勿論、監獄内から娑婆は見えない仕組みになってるぞ。
監獄なんで、何らかの制限が伴うのが普通の事だ。プロバイダーとかで、1台のサーバーに多数の同居人がいる場合、CPU時間を使い過ぎるユーザーがいると、他の人が迷惑する。制限をかけたいってのが要望として出て来る。
監獄で、メモリー制限してみる。
$ sudo rctl -a jail:j9:datasize:deny=300m
これ、監獄j9では、データメモリーサイズ(主として、data,bssだな)を、300Mに制限する例。それ以上は使えない。
g13@jail9:~/eisl-master % ./eisl Easy-ISLisp Ver0.91 > (quit) - good bye - g13@jail9:~/eisl-master % ./eisl Data segment size exceeds resource limit Abort
最初は制限をかけていなかったので、起動出来た。2回目は制限された状態での起動。メモリーを使い過ぎたんで、アプリの起動を拒否された。丁度、NetBSDでulimitの制限に引っかかったと同じ状態だ。
g13@jail9:~/eisl-master % ulimit -a g13@jail9:~ % ulimit -a cpu time (seconds, -t) unlimited file size (512-blocks, -f) unlimited data seg size (kbytes, -d) 33554432 stack size (kbytes, -s) 524288 core file size (512-blocks, -c) unlimited max memory size (kbytes, -m) unlimited locked memory (kbytes, -l) 64 max user processes (-u) 6656 open files (-n) 57870 virtual mem size (kbytes, -v) unlimited swap limit (kbytes, -w) unlimited sbsize (bytes, -b) unlimited pseudo-terminals (-p) unlimited
data seg sizeも十分過ぎる程大きいし、自分では制限無しに使えると思っているけど、見えない魔の手と言うか、ガラスの天井に覆われているんだな。
こういう制限を地道にかけていけば、CPU時間の無限消費とか、プロセスの無制限作成(いわゆる、fork爆弾)とかの、囚人による反乱を未然に防ぐ事が出来る。
また、リミット値を多めに設定しておいて、親側から監視する事も出来る。
$ sudo rctl -h -u jail:j9 cputime=5 datasize=473M stacksize=0 coredumpsize=0 memoryuse=473M memorylocked=0 maxproc=5 openfiles=0 vmemoryuse=573M :
上は、eislを動かしている時。下は、停止してアイドル状態。
$ sudo rctl -h -u jail:j9 cputime=5 datasize=7500K stacksize=0 coredumpsize=0 memoryuse=12M memorylocked=0 maxproc=4 openfiles=0 vmemoryuse=95M
at arm
ふとウブにarm環境としてラズパイもどきを1月に作った事を思い出した。IOポートが無いんで、Lチカ等な出来ないけど、eislの起動ぐらいは出来るだろう。やってみるべ。
pi@usvr:~/eisl-master $ uname -a Linux usvr 4.15.0-45-generic #48-Ubuntu SMP Tue Jan 29 16:28:13 UTC 2019 armv7l GNU/Linux
カーネルはウブのそれ。その中で、qemu-staticと言う仮想マシンを動かして、GNU/Linuxというユーザーランドが動いている。この機構は、上でやったFreeBSDでjailを動かすのと一緒。違いと言えば、armな石の環境ってのがある。
pi@usvr:~/eisl-master $ time make : real 0m52.878s user 0m52.019s sys 0m0.902s
コンパイルがもっさりしてたんで、時間を測ってみた。リアルなラズパイだと、どれぐらい時間がかかるのだろう? 興味あるな。それより、最近の豪華ラズパイを試してみろ。何でも、4CPU積んでるそうですから。
pi@usvr:~/eisl-master $ time make -j4 : real 0m26.507s user 1m26.198s sys 0m14.570s
vmwareは設定に4CPUまで使える。んな訳で、4CPUでよってたかってコンパイルする様に指示。 コンパイルが終了するまでの時間が半減した。(本当は1/4になって欲しいけど、諸般の事情で無理です)
尚、vboxはCPU数が、リアルに積んでる数の半分までしか指定出来ない。それやこれや吟味すると、オイラーのお勧め仮想化は、
VMWARE Player > VirtualBox >> WSL
と言う評価です。(VMWAREの回し者ではありません。主婦の友風な独断の評価なので、あしからず)
sakae@usvr:~$ ps awxl F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 4 0 23567 13398 20 0 4241656 10508 - Sl pts/1 0:00 /usr/bin/qemu-arm-static /bin/su -l pi 4 1000 23570 23567 20 0 4241120 15416 - Sl pts/1 0:00 /usr/bin/qemu-arm-static /bin/bash 0 1000 23906 23570 20 0 4241356 320848 - Sl+ pts/1 0:00 /usr/bin/qemu-arm-static ./eisl
こちらは、eislが動いている時、親側から、psした図。qemu-arm-staticで刑務所を実現してると言う理解で宜しいかな。
ああ、刑務所の中へeislのソースを持ち込む方法、色々あるな。オイラーは簡便にやった。刑務所が開いている時、親側から、ム所の /tmpに空輸(cpの事ね)。それを囚人にunzipさせた。
だって、ム所内から、回線(ssh)経由で、外から取り込むって、ム所の運営が破綻しますからね。