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のみ。他にどんな事が出来るの? 例えば、スレッドを切り替えるとか。 こういう時は、ネットを漁るに限る。
を教えて貰った。そんじゃ、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なんたらは、プロセス番号だったんだ。
いきなり本番のスレッド込みのコードを見ても、その幽玄さに圧倒されちゃうので、スレッドとは なんぞや? あたりから手を付けてみますかね。ネットに聞きに行きましたよ。
マルチスレッド・プログラミング(1) マルチスレッド・プログラミング(2)
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の事情はどうなってるのかな。後で調べてみるか。