newLISP(2)

『国勢調査 日本社会の百年』(岩波現代全書)なんて本を読んだ。国勢調査は5年毎に 全国一斉に行われる調査。今年がその年に当たっている。

10月1日深夜0時を持って、どこに居ましたか。どんな仕事に過去数ヶ月間ついていましたか? なんてのを調査する。

そもそもの始まりは、1920年だと言う。大正時代、露助との戦争に勝って、全国が浮かれていた頃だ。 人口の実態がどうなってるか調べたくても全国規模で行うと膨大な費用がかかる。そもそも 戸籍制度が有るんだから、それを使えばいいじゃんと言う強い抵抗勢力がある。

これを説き伏せない限り、全国規模の調査は無理。戸籍と実体がかけ離れているからこそ、 やりたい調査なのに。戸籍の移動をしないまま、あこがれの東京さ、出る人が多かった ため、戸籍と実体が一致しない。

で、戦争に勝って、これからは日本も世界に通用する一等国になるんだ。それには、実態 調査が是非必要って論理で、国勢調査の実現に漕ぎつけた。

国勢調査に混じらぬ人は死んだお方か影法師

この調査に洩れては国民の恥です

妾の家に泊まっていた旦那、今夜は国勢調査が有るんで、本宅へ帰りなさいと諭されたり、 深夜に調査するんで、そのための提灯を買う予算を下さいとか、笑えぬ話が有ったらしい。

近年では、個人情報の保護意識が浸透してきたり、オートロックマンションの普及や 供稼ぎの躍進で、いろいろ不都合が生じているらしい。なんで、お上に個人情報を 曝け出さなきゃならない、ってのも意識の低下に一役買ってるらしい。 どうせ、マイナンバーが始まれば、そんな戯れ言も意味無くなるんだけどね。

調査のリターンが問題。選挙とかだと、すぐに結果が判明するから、馴染みが有る んだけど、国勢調査の結果は忘れた頃にやってくる。マスコミの報道も地味だしね。

そこで、e-statですよ。 平成22年国勢調査 生データをCSVとかEXCELで提供するから、分析はご自由に。

BIG-DATAの材料には丁度いいな。結果を地図上にマッピング出来るんか。アドビの フラッシュなんて古い設備だなあ。早く、あんなBUG入りやめてHTMLだけで見られるように しておくれ。

データを落としてきてみようとしたら、面倒な手順を踏まされる。出来れば見て欲しくないって態度が、 如実に現れていますよ。プログラムを書いてスクライビングなんて、夢のまた夢とか思って いたら、オープンデータの推進なんてのが有った。 何やら、php,python,ruby,R とかの文字が躍っていたぞ。

こういうデータをバンバンと使われて、選挙の票配分が不平等ですとか、言われたく無い んだな。

また、スタバのない県とか、コンビニ率が極端に少ない県とかってTVに取り上げられて からかわれるのがイヤな、どこかの首長さんの差し金も入っているのかな。

みんなで、つついて、面白いデータを引っ張り出そうぜ。 色々な統計が公表されてるので、暇潰しにはもってこいですぜ。 統計学習サイトのなるほど統計学園が面白い。

gdb attach

Fedoraで動かしてるnewLISPにgdbを接続しようとして、以前失敗してた。

(gdb) attach 2085
Attaching to process 2085
warning: the SELinux boolean 'deny_ptrace' is enabled, you can disable this process attach protection by: (gdb) shell sudo setsebool deny_ptrace=0
ptrace: 許可されていない操作です.

こんな具合。でも、警告をよく見ると、SELinuxが邪魔してるから、コマンドを叩いて、 関所を開いてもらえとな。

(gdb) shell sudo setsebool deny_ptrace=0
[sudo] password for sakae:
(gdb) attach 2085
Attaching to process 2085
ptrace: 許可されていない操作です.

言われるままにやったんですが、まだ許可を得られず。第二関門があるんだな。ならば、rootで gdbを起動すればいいんでないかい。毎回やるのかったるい。そんな時は、

sudo chmod +s /usr/bin/gdb

gdbにrootの衣を着せるっていう手があるな。でも、それじゃ、直接的過ぎてつまんない。 OpenBSDでも同じ問題があって、あの時は man ptraceしたら解決策が提示された。 Fedoraはどうか? ptraceの改変の歴史が長々と語られているだけで、肝心な解決法の説明は 見当たらず。(流し見しただけなんで、書いてあったらスマソ)

んでもって、当たりを付けてみる。

[sakae@fedora newlisp-10.6.2]$ sysctl -a | grep ptrace
sysctl: permission denied on key 'fs.protected_hardlinks'
sysctl: permission denied on key 'fs.protected_symlinks'
sysctl: permission denied on key 'kernel.cad_pid'
sysctl: permission denied on key 'kernel.usermodehelper.bset'
sysctl: permission denied on key 'kernel.usermodehelper.inheritable'
kernel.yama.ptrace_scope = 1
sysctl: permission denied on key 'net.ipv4.tcp_fastopen_key'

一般ユーザーに閲覧出来る変数が見つかった。ならば、この変数の値を反転しちゃえ。 これも実験です。

[sakae@fedora newlisp-10.6.2]$ sudo sysctl kernel.yama.ptrace_scope=0
kernel.yama.ptrace_scope = 0
[sakae@fedora newlisp-10.6.2]$ gdb -q
(gdb) attach 2085
Attaching to process 2085
Reading symbols from /home/sakae/newlisp-10.6.2/newlisp...done.
Reading symbols from /lib/libm.so.6...(no debugging symbols found)...done.
  :

実験が成功しましたねぇ。後は、これを/etc/sysctl.dの中にあるファイルにでも書いておけば、 いいんだな。SELinuxの設定はどうすればいいのかな? 使う都度設定すればいいか。

sysctl

FedoraでもOpenBSDにも有った、sysctlってどうなってるの? この際首を突っ込んでみるか。 ソースもしっかりした説明もある、OpenBSDで確かめるのがよさそう。Linuxだと、いけいけ ドンドンで、説明書はなおざり、ソースは取り寄せないと無いという状況だけど、OpenBSDは 綺麗に揃っていますから。

まずはソース拝見って事で。/usr/src/sbin/sysctl/sysctl.c なんだけど、ぐちゃぐちゃ してて読みきれないので。。。

man sysctlすると、セクション8のやつが出てきた。SEE ALSOにsysctl(3)が載ってた。 systemcallになってるんだな。man 3 sysctlしてみる。

軽い説明の後、コードの断片が載ってたので、必要な部分を補って完成させたよ。

#include <sys/param.h>
#include <sys/sysctl.h>
#include <stdio.h>

main()
{
        int             mib[2], maxproc;
        size_t          len;

        mib[0] = CTL_KERN;
        mib[1] = KERN_MAXPROC;
        len = sizeof(maxproc);
        if (sysctl(mib, 2, &maxproc, &len, NULL, 0) == -1)
                err(1, "sysctl");
        printf("maxproc=%d\n", maxproc);
}

答え合わせしてみる。

[ob: z]$ ./a.out
maxproc=1310
[ob: z]$ sysctl kern | grep maxproc
kern.maxproc=1310

上のソースは、現在値を得るものだけど、NULL,0の所に新しい値をセットしてからcallすれば、 更新出来るとな。これだけ分かれば、複雑なsysctl(8)のソースも読むのはたやすいな。(大見得を切りましたよ)

次は、例のptraceがどう使われているかだな。sys/sysctl.hを見ると

#define KERN_GLOBAL_PTRACE      81      /* allow ptrace globally */

これを頼りに、カーネルのソース中をさ迷ってみる。

[ob: kern]$ grep -A 5 KERN_GLOBAL_PTRACE *
kern_sysctl.c:  case KERN_GLOBAL_PTRACE: {
kern_sysctl.c-          extern int global_ptrace;
kern_sysctl.c-
kern_sysctl.c-          return sysctl_int(oldp, oldlenp, newp, newlen, &global_ptrace);
kern_sysctl.c-  }
kern_sysctl.c-#endif

grepして見つかった行から5行分を表示って手抜きモードです。今度は、どうやらglobal_ptraceを 探せばよさそう。

[ob: kern]$ grep global_ptrace *
kern_sysctl.c:          extern int global_ptrace;
kern_sysctl.c:          return sysctl_int(oldp, oldlenp, newp, newlen, &global_ptrace);
sys_process.c:int       global_ptrace;  /* permit tracing of not children */
sys_process.c:          if (global_ptrace == 0 && !inferior(tr, p->p_p) &&

global_ptraceが出てきた、sys_process.cを見ます。sys_ptraceって、システムコールを 処理してるところね。

        case  PT_ATTACH:
                /*
                 * You can't attach to a process if:
                 *      (1) it's the process that's doing the attaching,
                 */
                if (tr == p->p_p)
                        return (EINVAL);
                  :
                /*
                 *      (4.5) it's not a child of the tracing process.
                 */
                if (global_ptrace == 0 && !inferior(tr, p->p_p) &&
                    (error = suser(p, 0)) != 0)
                        return (error);

attach出来ない条件として、(1) - (6) まで、6つ上げられているんだけど、4と5の間に 割り込んで、(4.5)って条件を付けて、禁止条項を作っているな。Fedoraでptraceの歴史が 長々と説明されてた訳だ。ソース自身が歴史の書だ。冒頭付近に

 *      from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
 */

/*
 * References:
 *      (1) Bach's "The Design of the UNIX Operating System",
 *      (2) sys/miscfs/procfs from UCB's 4.4BSD-Lite distribution,
 *      (3) the "4.4BSD Programmer's Reference Manual" published
 *              by USENIX and O'Reilly & Associates.
 * The 4.4BSD PRM does a reasonably good job of documenting what the various
 * ptrace() requests should actually do, and its text is quoted several times
 * in this file.
 */

こうやって、ソースを追うのが段々上達してくるな。

newLISP あれこれ

newLISPいいじゃんって事で、ちまちまと実験してる。

長いマニュアルを読むのもいいけど、 newLISP のコード用例 の方が、ずっと実例が出ててためになる。

前回やった、ベンチを複数回取るやつ。

(set 'ttl '())
(dotimes (n 5) (set 'ttl (cons (bench) ttl)))

consしてくんじゃなくて、pushの方がスマートだな。

(set 'ttl '())
(dotimes (n 5) (push (round (bench) -1) ttl))
total: (10865 10979.5 11037.6 10928.1 10948.7)
mean: 10951.78
std:  63.70727587994477
std/mean 0.5817070456121725

つらつらとモジュールの中を見てたら、README.txtに、マニュアル用のhtmlの作成方法が 出てた。単純に、newlispdoc hoge.lspすれば、hoge.lspの中のコメントを抜き出して、 それをhtmlにしてくれるようだ。

たとえば、unix.lspを対象に選ぶと、unix.lsp.htmlってのとindex.htmlが作成された。 index.htmlをw3mで開いてみると

                                       Index


    Module: unix.lsp

    Interface to various UNIX libc functions

    getuid    geteuid    getgid    getegid    setuid    seteuid    setgid
    setegid    kill    chmod    ioctl    mkfifo    mktemp    syslog

                                       - ∂ -

                       generated with newLISP  and newLISPdoc

それぞれがリンクになってて、これはクリック猿御用達だな。この元となったunix.lspも 見ておくか。

ふむ、コメント記号のセミコロンを2つ重ねた後が表示されるんだな。そして若干の強調を する為に、@syntax みたいに、あっとマークを前置するとな。こういうの流行してるね。

むむ、更新忘れを発見したぞ。

(set 'files (list
    "/usr/lib/libc.dylib" ; MacOS/Darwin
    "/usr/lib/libc.so.51.0" ; OpenBSD 4.6
    "/lib/x86_64-linux-gnu/libc.so.6" ; Ubuntu 12.04 LTS
    "/lib/i386-linux-gnu/libc.so.6" ; UBUNTU Linux
    "/lib/i686-linux-gnu/libc.so.6" ; UBUNTU Linux
    "/lib64/libc.so.6" ; CentOS 6.x
    "/lib/libc.so.6" ; UBUNTU Linux 9.04
    "/usr/lib/libc.so" ; Linux, BSD, Solaris
))

OpenBSD 5.7のlibcは、随分とバージョンが進んでいるよ。

[ob: ~]$ locate libc.so
/usr/lib/libc.so.78.1

ものは試しに、どんなエラーになるか、やってみる。

> (module "unix.lsp")

ERR: user error : cannot find standard C library (libc)
called from user function module

修正したら

> (module "unix.lsp")
(lambda (unix:priority unix:message) (zero? (unix:_syslog (int unix:priority 0) (
    string unix:message))))
> (unix:getuid)
1000

おおげさな割りに、使いたいのが無いな。たとえば、getpidとかね。直接、importしちゃえば いいのかな。でも、unix.lspをみると、何か仕掛けが施されているな。後で調べてみよう。 ああ、crypto.lspにもバージョン違いのlibcrypto.soが有ったぞ。

(3 (main-args))

手続き名が来る位置に数値とは、これいかに?

(rest (rest (rest (main-args))))

らしいです。ぶったまげたーーー・ 更に

> (set 'x "abcdefg")
"abcdefg"
> (3 x)
"defg"
> (3 2 x)
"de"
> (-3 x)
"efg"
> (x 3)
"d"

newLISP and gnuplot

newLISPがgnuplotと繋がるか調べてみる。それに使えそうなやつがexampleに出てた。

[ob: examples]$ cat observer
#!/usr/bin/env newlisp

(define (count-down-proc x channel)
  (while (!= x 0)
      (write-line channel (string x))
      (dec x)))

(define (observer-proc channel)
  (do-until (= i "1")
    (println "process " (setq i (read-line channel)))))

(map set '(in out) (pipe))
(set 'observer (fork (observer-proc in)))
(set 'counter (fork (count-down-proc 5 out)))

; avoid zombies
(wait-pid observer)
(wait-pid counter)

(exit)

2つの関数を定義、channelってschemeで言うportの事だな。肝はその次の3行。パイプを 作ってる。パイプだから両端がある訳で、それぞれin,outって名前を付けてるんだな。 こういう使い方をマルチバインドって言うんか。 なお、パイプには情報が流れて行くんだけど、方向性を持ってる。最初が(read)で、 次のが(write)だな。

次の2行は、先に定義した関数を子プロセスとして起動するんだな。関数の戻り値としてpidが 返るので、それに名前を付ける。

最後は、子プロセスが終了するのを待つ。自分が親で、子を2つ作って、子同士をパイプで 結んで、やりとりさせるって言う、豪華版です。

今回欲しいのは、親はgnuplotに与えるデータを用意し、子のgnuplotにそのデータを投げて グラフを書かせるってやつ。こういう時は、processってのを使えとスニペットが言ってた。

早速マニュアルを参照。ぴったりな例が出てたぞ。process。第一引数は、起動するアプリ。 絶対PATHで指定しろとな。それがいやなら

(process (first (exec "which xclock")))  → 22607 ; on Unix

こういう技を使えとな。返ってくるのは pidか。パイプを併用する例が出てた。

;; create pipes
(map set '(myin bcout) (pipe))
(map set '(bcin myout) (pipe))

;; launch Unix 'bc' calculator application
(process "/usr/bin/bc" bcin bcout) → 7916

(write-line myout "3 + 4")  ; bc expects a line-feed

(read-line myin)  → "7"

プロセスの入出力用に2本パイプを用意し、その片方づつを、親側(newLISP)に接続するとな。 パイプに書き込む時は、write-line、プロセスからのデータを受け取る時は、read-lineを 使うとな。なかなか壷にはまった説明だな。

上の例ではプロセスが出す、エラー出力は受けてないけど、もう一つパイプを追加作成して、 それをエラー受け取り用にし、peekってのを使って、モニター出来るみたいだ。 ほかにも、注意書きが有ったから読んどけ。なかなかマニュアルが親切で良いぞ。

(set 'gpcmd [text]
plot '-' w l
0 3
1 2
2 4
3 1
[/text] )

(exec "gnuplot  --persist" gpcmd)
(exit)

マニュアルを良く読んでいたら、もっと簡単な方法のヒントがexecの所に出てた。第二引数に 文字列を取れて、それがプロセスに流し込まれるとな。gnuplotからのエラーを見なかった事にすれば、 これで十分。

また、文字列を作るにも、ヒア・ドキュメントが使えるって、まさにスクリプト言語の 風格十分だな。

おまけ

vmware-playerとvboxに同じFreeBSDが入ってたんで、例のnewlispのベンチをやってみた。 まずは、vmwareの方

total: (5424.7 5397 5434.4 5788.1 5885.7)

    N        =     5
    mean     =  5585.98
    avdev    =   200.74
    sdev     =   232.05
    var      = 53846.44
    skew     =     0.33
    kurtosis =    -2.16

こちらは、バーチャルボックスの方

total: (6508.1 6536.7 6425.1 6461.8 6405.2)

    N        =     5
    mean     =  6467.38
    avdev    =    44.02
    sdev     =    55.11
    var      =  3037.12
    skew     =     0.10
    kurtosis =    -2.05

更に、vmwareとかvboxを動かしている土台のWindows7

total: (6047.6 6155.1 6070.1 6010.1 6075.1)

    N        =     5
    mean     =  6071.60
    avdev    =    34.80
    sdev     =    53.26
    var      =  2836.25
    skew     =     0.44
    kurtosis =    -1.43

マニュアル見てたら、statsって言うのが有ったので使ってみたかっただけ。 いきなり、分布の歪度(skew)とか分布の尖度(kurtosis)なんていう、高級な統計用語が出て きて、面食らっています。で、調べてみたよ。 正規分布の歪度や尖度

avdevは平均偏差、個々のデータと平均値の差を取り、絶対値に変換してから足して、それを 平均値に割ったもの。sdevは、差を2乗してから計算し、最後に平方根(2乗したから)を 求めてる。

わざわざ2乗なんてするより絶対値の方が計算楽じゃんと思ってたけど、みなさんも同じ 疑問があるようです。 標準偏差と平均偏差の違い とか、 標準偏差と平均絶対偏差の違いについて適切な解説をしてください が繰り返されているな。

vboxには、newLISPを インストールしてないので、本体とベンチ用スクリプトだけを転送して実験。本体だけで 結構色々な事が出来るので、bashと試合しても勝てるぞ。