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)経由で、外から取り込むって、ム所の運営が破綻しますからね。