racket (2)

読みたい本が有ったので、図書館の蔵書検索パソコンを初めて使った。キーボードが 付いていない。画面に50音表が出てきて、それにタッチしろとな。ちょいと焦ったぞ。 ipadの50音キーボードを殺している身としては。そうか、ipad御用達の老人向けなんだな。

目指す本は古かったので倉庫に眠っているとか。おねーちゃんの手を煩わせてしまったな。 借りてきたのは、 『カッコウはコンピュータに卵を産む(上・下)』(草思社)。発行が1991年のVer7でした。

この本、オイラーがunixを始めた頃に一度読んだ事があった。書いてある技術的な事は さっぱりだったけど、今回は楽しんで読めた。

カリフォーニアにあるローレンス・バークレー研究所が舞台。新米unix管理者が、75セントの 課金違いを調査されるように命じられる所から話が始まる。彼の両隣にいるベテランは、unix命って人と、 VAXのVMS以外は認めんというDEC Loveな人。間にたった新人は大変。

VMSは勿論メーカーからの供給OS。それに逆らって、4.2 BSDをVAXに入れてるって、現代にも 通じるな。Windowsなんて屁なOS。そんなの潰してLinuxでも入れちゃえってね。何たって、 BSDのお膝元ですからねぇ。ソースの無いOSなんて使っていられるか。その気持ち、よーく 分かりますよ。

75セントの課金違いは、よそからの不正ログインによるものだった。新米君は、この不埒な やっこさんを監視する事にする。不埒はやつは用心深いので、OS上で監視って訳にはいかない。 で、考えたのは、モデムとターミナルを結ぶケーブルの間にプリンターを入れて、タッピング する方法。これなら絶対に相手に分からない。

不埒なやつがどうやってroot権を奪取するかみてると、GNU-emacsに付属のメールユーティルティーの 虚を突き、atrunのスクリプトをシステム領域に転送。これで、root権が得られる。これって、 RMSのおおらかな所丸出し、確信犯的な穴だな。

この方法を著者の新米君は、カッコウの託卵になぞらえて、本の題名にしてる。 カッコウは自分の卵をよその鳥の巣に置き、卵を孵させる習性があるとか。atrunのスクリプトを システムに預け、じっとroot権限で動き出すのを待つ。粋な例えだな。

やがて、不埒は奴は、軍のコンピューターにも魔手を伸ばし始める。新米君は、3文字の政府機関 (FBIとかCIAとか)に協力を求めるが、暖簾に腕押し状態。そうでしょう、そうでしょう、 役人は自分の島をがっちりと固めていますからねぇ。

不埒な輩はやがて、カーミットを使って、プログラムやら重要文書やらを自分のコンピュータに 転送しだす。それを見てた新米君、いてもたってもいられず、信号端子をちょいとショート させる技に出た。RS232Cのラインに雑音を混入させたって訳だな。

こんな事して大丈夫か? RS232Cの規格では、プラス・マイナス5Vでスイングする信号って 決められてるんだけど、それをショートだから0Vにしたんか。スイング範囲内の電圧だし、 当然保護回路が付いている だろうから、問題なしかな。

パスワードファイルの辞書攻撃とか、デフォルトのパスワードを狙うとか、今でも十分に 通用する悪い方法が目白押しで楽しめましたよ。

エピローグでNSAの長官の息子が放った、ワームの話が出てきた。sendmailの穴とfigerの穴を 突いて、ワームを増殖させる。この話も面白かったぞ。そうそう、emacsの穴に対抗して、 vi(recover)の穴も開陳されてて、これには笑わせて貰いましたよ。ああ、オイラーも不埒だな。

5冊 / 1504頁 / 9200円

drracket

前回、drracketを実行した時、実質的にgracketが実行されてた。という事は、drracketは ラッパーなんだな。どんな風に実行されてくか、traceしてみた。(実験は、ウブに自前で入れた、drracketです)

sakae@uB:~$ sh -x src/racket-6.0.1/bin/drracket
+ saveP=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
+ PATH=/usr/bin:/bin
+ pwd
+ saveD=/home/sakae
+ dirname src/racket-6.0.1/bin/drracket
+ D=src/racket-6.0.1/bin
+ basename src/racket-6.0.1/bin/drracket
+ F=drracket
+ cd src/racket-6.0.1/bin
+ test -L drracket
+ pwd
+ D=/home/sakae/src/racket-6.0.1/bin
+ cd /home/sakae
+ bindir=/home/sakae/src/racket-6.0.1/bin
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
+ librktdir=/home/sakae/src/racket-6.0.1/bin/../lib
+ findxend
+ oneargflag=
+ exec /home/sakae/src/racket-6.0.1/bin/../lib/gracket -N src/racket-6.0.1/bin/drracket -J DrRacket -l- drracket/drracket.rkt

racketをソースから(14.04 LTS編)

例によってソースから入れてみるって儀式を行う。ソースはracketのHPから採れる。 18Mぐらいに圧縮してあるけど、うぶで展開したら、55Mぐらいになった。

ソースからのコンパイル手順は、src/READMEに書いてある。

 From this directory (where the `configure' file is), run the following
 commands:

   mkdir build
   cd build
   ../configure
   make
   make install

たったこれだけである。オイラーのおんぼろマシンでは、makeに約15分かかった。installは さっぱり終わらん。何故? メモリーが足りなくてswapを使い出したものだから、遅くなってた。 VMWareへのメモリー割り当てをどーんと(1.5G)増やしてあげたら、すいすい行って、1時間 ちょっとかかった。

sakae@uB:~/src/racket-6.0.1/bin$ ls
drracket      mred-text  mztext      plt-r5rs        raco       slideshow
gracket       mzc        pdf-slatex  plt-r6rs        scribble   swindle
gracket-text  mzpp       plt-games   plt-web-server  setup-plt
mred          mzscheme   plt-help    racket          slatex

ごちゃごちゃあるけど、大事なのはracketで、後は大体ラッパーだったよ。

折角ソースを取り寄せたので、おまけの例が無いかとさがしてみたけど、それは見当たらなかった。 ちょっと残念。

racketをソースから(FreeBSD編)

気を良くしたおいらは、15年物のノーパソにも入れてあげる事にした。 どうせなら、Linuxと同じバージョンのracketがいいよね。

その前にportsのMakefileを参考までに読んでみた。(racket 5.9用だけど大差ないだろう) そしたら、こんな脅かしが出てきた。メモリー沢山必要ね。

.if ${ARCH} == "i386"
MANUAL_PACKAGE_BUILD=   i386 requires kern.maxdsiz="640M" to build
.endif

gmakeは我慢して終了させた。でも、続いてinstallしたら、binの下にracketは出来たんだけど、 後はDISKががりがり言うだけなんで、インストール中止。どうせ、racketしか起動 しないんだから。。と言うか、ソース閲覧ツアーするだけですから。。

ソース閲覧ツアー

手っ取り早く、ソース閲覧するには、gdbにかけてトレースだな。でもstripされてたぞ。 これじゃ面白さが半減しますよ。configureのオプションを調べたら、標準でstripする ようになってた。こりゃ、configureからやり直しかな? まてまて、念の為に Makefileを覗いてみるか。

unix-install-3m:
        cd ..; $(ICP) racket/racket3m "$(DESTDIR)$(bindir)/racket"
        cd ..; $(STRIP_DEBUG) "$(DESTDIR)$(bindir)/racket"

stripを頼りに調べてみたら、debugに便利なracket3mが幸いにも残ってますよ。

emacs上からgdb racket3mしてステップしてくと

(gdb) b main
Breakpoint 1 at 0x8072610: file ../../../racket/gc2/../main.c, line 336.
(gdb) run
Starting program: /home/sakae/src/racket-6.0.1/src/build/racket/racket3m
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
(gdb) n
(gdb)
[New Thread 0xb729bb40 (LWP 1131)]
(gdb) n
  :
(gdb)
Single stepping until exit from function __kernel_vsyscall,
which has no line number information.
(gdb) bt
#0  mprotect () at ../sysdeps/unix/syscall-template.S:82
#1  0xb6610000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)

こういうメッセージが出てきて、言う事を聞かなくなっちゃったぞ。その時に ソース窓は、vm.cになってて

static void mmu_write_unprotect_page(MMU *mmu, void *p, size_t len) {
  mmu_assert_os_page_aligned(mmu, (size_t)p);
  mmu_assert_os_page_aligned(mmu, len);
=>os_protect_pages(p, len, 1);
}

な所を表示してた。もうツアーはここで打ち切りでしょうかね? なんか置いてきぼりを 喰らった気分ですよ。

置いてきぼり、置いてきぼり、、、ならば途中参加はどうだ。実社会で言うと、ツアーバスの 出発に間に合わなかったんで、後からフェアレディーZで追いかけて、途中からツアーに合流 するって手があるな。(やった事はないけど)

仮想世界だと、racket3mを起動しといて、後からgdbでアタッチする技。旨く行くかやってみんべ。 だめ元承知でね。

sakae@uB:~$ gdb -p 1085
GNU gdb (Ubuntu 7.7-0ubuntu3.1) 7.7
  :
Attaching to process 1085
Could not attach to process.  If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.

途中参加は駄目って断ってきたな。でも裏から手を回せば何とかなりそうな雰囲気。 /etc/sysctl.d/10-ptrace.confの設定を変えちゃえ。

# In general, PTRACE is not needed for the average running Ubuntu system.
# To that end, the default is to set the PTRACE scope to "1".  This value
# may not be appropriate for developers or servers with only admin accounts.
kernel.yama.ptrace_scope = 0

普通じゃ無い人になりましょ。この場合設定変更して、どのダエモン君を再起動すれば 良いんでしょうか? よう分からんので、システムごと再起動したぞ。(こういう所が、 素人丸出しの、スクリプト・キッディ(ちゃうで、爺だろに)です。

sakae@uB:~$ gdb -p 1190
Attaching to process 1190
  :
[New LWP 1191]
  :
(gdb) info threads
  Id   Target Id         Frame
  2    Thread 0xb69bbb40 (LWP 1191) "racket3m" 0xb76fd424 in __kernel_vsyscall   ()
* 1    Thread 0xb74d8d40 (LWP 1190) "racket3m" 0xb76fd424 in __kernel_vsyscall   ()

今度は旨くいって無事にアタッチ出来た。おいらがgdbのスレッド操作に付いて知ってる事は、 info threadsのみ。他にどんな事が出来るの? 例えば、スレッドを切り替えるとか。 こういう時は、ネットを漁るに限る。

thread debug

を教えて貰った。そんじゃ、debug対象を切り替えてみるか。

(gdb) thread 2
[Switching to thread 2 (Thread 0xb69bbb40 (LWP 1191))]
#0  0xb76fd424 in __kernel_vsyscall ()
(gdb) bt
#0  0xb76fd424 in __kernel_vsyscall ()
#1  0xb7693d4b in pthread_cond_wait@@GLIBC_2.3.2 ()
    at ../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/pthread_cond_wait.S:187
#2  0x081b328c in green_thread_timer (data=data@entry=0x8edc370)
    at ../../../racket/gc2/../src/port.c:10907
#3  0x08162be2 in mzrt_thread_stub (data=0x8edc3e8)
    at ../../../racket/gc2/../src/mzrt.c:170
#4  0xb768ff70 in start_thread (arg=0xb69bbb40) at pthread_create.c:312
#5  0xb75c670e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:129

副スレッドはcloneなんてのが最上位になってるな。これって、どこかで聞いた事があるぞ。 まあ、それは後で調べるかな。

(gdb) thread 1
[Switching to thread 1 (Thread 0xb74d8d40 (LWP 1190))]
#0  0xb76fd424 in __kernel_vsyscall ()
(gdb) bt
#0  0xb76fd424 in __kernel_vsyscall ()
#1  0xb75b77ab in poll () at ../sysdeps/unix/syscall-template.S:81
#2  0x081b35ab in default_sleep (v=0, fds=0xb5d2c0e0)
    at ../../../racket/gc2/../src/port.c:10683
#3  0x08254795 in check_sleep (need_activity=need_activity@entry=1,
    sleep_now=sleep_now@entry=1) at ../../../racket/gc2/../src/thread.c:4120
#4  0x0825ec89 in scheme_thread_block (sleep_time=0)
    at ../../../racket/gc2/../src/thread.c:4838
#5  0x08260522 in scheme_block_until (_f=_f@entry=0x820b350 <out_of_line>,
    fdf=fdf@entry=0x0, data=0xb5189f50, delay=<optimized out>, delay@entry=0)
    at ../../../racket/gc2/../src/thread.c:4974
#6  0x0820dab6 in scheme_wait_semas_chs (n=1, o=0xb5189de8,
    just_try=just_try@entry=0, syncing=0xb5189e00)
    at ../../../racket/gc2/../src/sema.c:778
     :
#50 main_after_stack (data=0xbf8f4068) at ../../../racket/gc2/../main.c:450
#51 0x08076878 in do_main_stack_setup (
    no_auto_statics=no_auto_statics@entry=1,
    _main=_main@entry=0x8074440 <main_after_stack>, data=data@entry=0xbf8f4068)
    at ../../../racket/gc2/../src/salloc.c:198
#52 0x08076921 in scheme_main_stack_setup (no_auto_statics=1,
    _main=_main@entry=0x8074440 <main_after_stack>, data=data@entry=0xbf8f4068)
    at ../../../racket/gc2/../src/salloc.c:310
#53 0x08072643 in main_after_dlls (argv=0xbf8f4114, argc=1)
    at ../../../racket/gc2/../main.c:381
#54 main (argc=1, argv=0xbf8f4114) at ../../../racket/gc2/../main.c:341

随分とスタックが伸びているな。こりゃ、観光のしがいがあるぞ。emacs上でも、gdb -p は、 使えるんだろうな。

threadsのお勉強

その前に、スレッドの勉強をしてみるかな。どうもスレッドって、プロセスと同じような感覚が 有るけど、どうよ。上に出てきたcloneもプロセスを複製するシステムコールだったように 記憶してる。

でも、psで見るとracketのプロセスは一つしか見えない。ふと、プロセスのツリーを 表示するコマンドが有る事を思い出したぞ。

sakae@uB:~$ ps awx|grep racket
 1091 tty1     Sl+    0:01 racket
sakae@uB:~$ pstree -p
init(1)─┬─cron(751)
         ├─dbus-daemon(462)
         ├─dhclient(575)
         ├─getty(725)
         ├─login(847)───bash(1077)───racket(1091)───{racket}(1092)
         :

ピンポン。隠れプロセスが居た。LWPなんたらは、プロセス番号だったんだ。

いきなり本番のスレッド込みのコードを見ても、その幽玄さに圧倒されちゃうので、スレッドとは なんぞや? あたりから手を付けてみますかね。ネットに聞きに行きましたよ。

mmap()の使い方

マルチスレッドのコンテキスト切り替えに伴うコスト

マルチスレッド・プログラミング(1) マルチスレッド・プログラミング(2)

GNU/Linux でのスレッドプログラミング

RHG 第19章 スレッド なんて懐かしいんだ。参考にしましょ。

折角なので、上記のスレッドサンプルをコピペして、挙動を探ってみる。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_t thread;

void* thread3 (void* d) {
  int count3 = 0;
  while(count3 < 1000){
    sleep(3);
    printf("Thread 3: %d\n", count3++);
  }
  return NULL;
}

void* thread2 (void* d) {
  int count2 = 0;
  while(count2 < 1000){
    sleep(3);
    printf("Thread 2: %d\n", count2++);
  }
  return NULL;
}

int main (){
  pthread_create(&thread, NULL, thread2, NULL);
  pthread_create(&thread, NULL, thread3, NULL);
  //Thread 1
  int count1 = 0;
  while(count1 < 2){
    printf("mainThread : %d\n", count1++);
  }
  pthread_join(thread,NULL);
  return 0;
}

こんなコードをコンパイルして、俎上に乗せてみた。

sakae@uB:~/src/th$ cc -g th.c -lpthread
sakae@uB:~/src/th$ strace ./a.out
sakae@uB:~/src/th$ pstree -p
init(1)─┬─cron(753)
         :
         ├─login(848)───bash(1159)───a.out(1180)─┬─{a.out}(1181)
                                                          └─{a.out}(1182)

トレース結果は?

brk(0)                                  = 0x88f0000
brk(0x8911000)                          = 0x8911000
mprotect(0xb6de2000, 4096, PROT_NONE)   = 0
clone(child_stack=0xb75e2424, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb75e2ba8, {entry_number:6, base_addr:0xb75e2b40, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb75e2ba8) = 1181
mmap2(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xb65e1000
mprotect(0xb65e1000, 4096, PROT_NONE)   = 0
clone(child_stack=0xb6de1424, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb6de1ba8, {entry_number:6, base_addr:0xb6de1b40, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb6de1ba8) = 1182
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77b9000
write(1, "mainThread : 0\n", 15mainThread : 0)        = 15
write(1, "mainThread : 1\n", 15mainThread : 1)        = 15
futex(0xb6ddfba8, FUTEX_WAIT, 1234, NULLThread 3: 0
Thread 2: 0
Thread 3: 1

ああ、これじゃ子スレッドは追跡してくれていなかった。strace -f すればいいんだ。

futex(0xb6d8fba8, FUTEX_WAIT, 1259, NULLProcess 1259 attached
 <unfinished ...>
[pid  1259] set_robust_list(0xb6d8fbb0, 12) = 0
[pid  1259] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
[pid  1259] rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
[pid  1259] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid  1259] nanosleep({3, 0}, Process 1258 attached
 <unfinished ...>
[pid  1258] set_robust_list(0xb7590bb0, 12) = 0
[pid  1258] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
[pid  1258] rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
[pid  1258] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid  1258] nanosleep({3, 0},  <unfinished ...>
[pid  1259] <... nanosleep resumed> 0xb6d8f17c) = 0
[pid  1259] write(1, "Thread 3: 0\n", 12Thread 3: 0) = 12

追試(懐かしい、いやな言葉だな)したんで、ちょっとPIDが違うけど、子スレッドの 挙動が分かるな。

大事なのは、cloneで子スレッドを作る時に、それ用のスタックを指定してる事かな。

thread in FreeBSD

余勢を駆って、FreeBSDのスレッド事情も偵察

[sakae@fb10 ~/src/th]$ truss ./zz
   :
mmap(0xbf8fd000,1052672,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_STACK,-1,0x0) = -1081094144 (0xbf8fd000)
mprotect(0xbf8fd000,4096,PROT_NONE)              = 0 (0x0)
thr_new(0xbfbfd7f0,0x34,0x2806fe80,0x2805f2b0,0xbfbfd810,0x2804c5fd) = 0 (0x0)
mmap(0xbf7fc000,1052672,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_STACK,-1,0x0) = -1082146816 (0xbf7fc000)
mprotect(0xbf7fc000,4096,PROT_NONE)              = 0 (0x0)
thr_new(0xbfbfd7f0,0x34,0x2806fe80,0x2805f2b0,0xbfbfd810,0x2804c5fd) = 0 (0x0)
fstat(1,{ mode=p--------- ,inode=1,size=0,blksize=4096 }) = 0 (0x0)
Thread 1: 0
Thread 1: 1
  :

cloneの代わりにthr_newってのが担当してるのか。manで探してみたけど該当無し。 本当にそんなシステムコールが有るんかいなと思って、sys/syscall.hを調べると、

#define SYS_thr_create  430
#define SYS_thr_exit    431
#define SYS_thr_self    432
#define SYS_thr_kill    433
#define SYS_thr_new     455

こんな具合に存在してた。番号を大きいって事は、後から追加された新人って事だな。 (いつの時代も社長の社員番号は1番って言う、例外は有るけどね。)

しょがないので、-lpthreadのソースを /usr/src/lib/libthr/thread/thr_create.cあたりで眺めてみるかな。

それとも、いきなり本丸に迫ってみるか。地図は /usr/src/sys/kern/{kern_kthread.c, kern_thr.c, kern_thread.c}この辺かな。 注意しないと、カーネルスレッドとユーザースレッドが有る事だな。まあ、さして違いは 無いと思うけど。。。

それにしても、BSD系はマニュアルがしっかりしてると思っていたけど、事情は変わったの かな。それとも、ソースに勝るマニュアルは有りませんと主張してるのかな。

OpenBSDの事情はどうなってるのかな。後で調べてみるか。