オペ―レーティングシステムの読み書き Rev.2.1 @magoroku15 2012/3/24 日本Androidの会 沖縄支部 workshop@Naha Vol.17 - v6 unix 勉強会 2012/3/24 1
他人の作品を読まなかった偉 大な作家,他人の筆づかいを 研究しなかった偉大な画家, 同僚の肩越しに技を盗まな かった腕のよい外科医,副操 縦席で実地の経験を積まな かった767機長――果たして, そんな人たちが本当にいるの でしょうか? Dave Thomas著 Code Reading―オープンソースから学ぶソフトウェア開発技法 より 2012/3/24 2
最底辺活動  Lions本読書会に出てみた  本だけで読み解くのは大変  若干の解説を加えれば理解できる人は増えるはず  拙者のネタとしてGeekBarなどで話すことにした  これを最底辺活動と言っている ○ 他にもFPGAでPDP11を動かしたり、PICで遊んだり  UNIX v6を読む理由  最新のBSD, Linuxと実装は異なるが根幹は同じ  UNIX v6を読む事が現代のOSを理解する近道(だと 思う) 2012/3/24 3
Lions’ Commentary on UNIX本読書会  月に1回貸し会議室(喫茶室ルノアール)にあつまってLions本の読書会  濃すぎる常連 @awazo 主催している人 @7shi 低レイヤ勉強会を多数主催、SilverLightで動くPDP-11のエミュレータ作った人 @xylnao11 古くからPDP-11とUNIXで遊んでて、最近安価なFPGAにPDP-11を載せてしまった人 @superhoge Blogがすごい、JavaScriptで動くPDP-11のエミュレータを作った人 @toyoshim 一人で読み終えてしまった人 @pakuqi v7講座を生徒役で参加してBlogに話した事の10倍くらいの内容をまとめてくれた人 @oraccha PDP11に詳しくて、V1も読んでる人 2012/3/24 4
お勧めのBlogなど  http://d.hatena.ne.jp/takahirox/  http://www.tom-yam.or.jp/2238/index.html  http://d.hatena.ne.jp/n7shi/  http://xiangcai.at.webry.info/  http://d.hatena.ne.jp/oraccha/ 2012/3/24 5
@magoroku15の最近のネタ  ADK互換モジュールを1200円で作る http://www.slideshare.net/magoroku15/poormans-adk-11350123  OpenCoresのw11を動かしてみる http://www.slideshare.net/magoroku15/pdp11-onfpga  その他  横浜Android PF部,nagoyageekbar等で話したネタ は以下に置いてあります ○ http://www.slideshare.net/magoroku15/ 2012/3/24 6
教材  Unix version6  テキスト ○ Lions Commentary on UNIX / ASCII Books 3990円  原著online版 ○ http://v6.cuzuco.com/v6.pdf ○ http://www.lemis.com/grog/Documentation/Lions/boo k.pdf  PDP-11/40  PDP-11 Processor Handbook  http://pdos.csail.mit.edu/6.097/readings/pdp11-40.pdf 2012/3/24 7
ウォームアップ  OSの領域はプログラマにとっての最底辺部  アプリケーションプログラマ ○ 提供されるAPIを利用して、一般向けのサービス等を提供する人  システムプログラマ ○ ハードウェアの機能を利用して、API等プログラマ向け機能を提供 する人  必要とされる前提知識が異なる  命令レベルで計算機を(ぼんやりと)理解する必要あり  アプリケーションプログラマ向けの準備運動  Lions本 ○ 第2章 Fundamentals (PDFだとP4) 命令セット ○ 第9章 Hardware Interrupt and Traps (PDFのP42) 割り込み  PDP-11 Processor Handbook 2012/3/24 8
Lions本 2章 2012/3/24 9
システムプログラマから見た 計算機の構成要素  CPU  レジスタ ○ 汎用レジスタ 一般の演算命令に使用 ○ スタックポインタ スタックの先頭位置 ○ プログラムカウンタ 次に処理する命令の位置  フラグレジスタ 演算結果の一部を記憶  制御/状態レジスタ  Memory  外部記憶 2012/3/24 10
PDP-11/40  レジスタ  16bitのレジスタが8個  r0-r7  r6はspスタックポインタ  r7はpcプログラムカウンタ  Processor Status Word  フラグレジスタ+状態レジスタ  memory  仮想アドレス幅は16bit 最大 64kbyte  物理アドレス幅は18bit 最大 256kbyte 2012/3/24 11
レジスタ r0-r7 r0, r1 演算結果の一時的な保持、復帰値など r2, r3, r4 関数内のローカル変数。呼ばれた関数で退避し、 関数の終了時に復元 R5 フレームポインタ r6 (sp) スタックポインタ r7 (pc) 実行する命令の位置(アドレス) 2012/3/24 12
Processor Status Word (PSW) 14-15 現在のモード (00 = kernel 11 = user) 12-13 以前のモード 5-7 プロセッサの優先度 (0~7) 4 トラップビット 3N 演算結果が負 2Z 演算結果が0 1V 演算結果がオーバフロー 0C 演算結果がキャリー 2012/3/24 13
CPUの処理  以下を繰り返す 1. PCの示す位置(アドレス)から命令を読み込む 2. 解析して 3. 実行 4. 実行の副作用として以下の事象が発生する  PSWを書き換え  PCを書き換え  レジスタの書き換え  メモリの内容を書き換えたりする 2012/3/24 14
CPUの処理 誤解しやすい点  PCもレジスタの一種  PC(r7)を書き換えると処理がジャンプ  比較と分岐は別命令 2012/3/24 15
UNIXカーネルを理解するための 2012/3/24 16
C言語の空間モデル  Text text  命令列の格納領域  書き換え禁止 data  Data bss  非0で初期化されたデータ領域  Bss  0で初期化されたデータ領域  Stack  自動(auto)変数の格納領域  関数呼び出し時のレジスタ退避域 stack 注)スレッドは、まだ存在しないので対象外 2012/3/24 17
変数の宣言と空間配置 int i; // Bss a.out形式 実行時 (ファイル) (メモリ) Int j = 1; // Data header main() text text { data data static int k = 1; // Data int l = 1; // Stack bss int m; // Stack : } stack 2012/3/24 18
関数呼び出しとスタック C main() { int e, f, r; e = 1; f = 2; r = func(e, f); } int c, d; func(a, b) { c = a; d = b; return c + d; } 2012/3/24 19
関数とスタック namelist # nm -n a.out 000170T cret 0x78 000000a 0x00 crt0.o 000206B _c 000000t start 000210B _d 000004a a 000212B savr5 000006a b 177764a r 000030T 0x18 _main 177766a f 000030a src.o 177770a e 000030t ~main 000102T 0x42 _func 000102t ~func 000140T 0x60 _exit 000140a exit.o 000152T 0x6a csv 000152a csv.o 2012/3/24 20
関数呼び出しとスタック ASM .globl _main .comm _c,2 .text .globl _d _main: .comm _d,2 ~~main: .globl _func ~e=177770 .text ~f=177766 _func: ~r=177764 ~~func: jsr r5,csv ~a=4 sub $6,sp ~b=6 mov $1,-10(r5) jsr r5,csv mov $2,-12(r5) mov 4(r5),_c mov -12(r5),(sp) mov 6(r5),_d mov -10(r5),-(sp) mov _c,r0 jsr pc,*$_func add _d,r0 tst (sp)+ jbr L2 mov r0,-14(r5) L2: jmp cret L1: jmp cret .globl .globl _c .data 2012/3/24 21
csv 関数の入り口 Main: csv: : mov r5,r0 jsr r5,csv mov sp,r5 : mov r4,-(sp) mov r3,-(sp) mov r2,-(sp) tst -(sp) jmp (r0) r0 r1 r2 r3 r4 r5 r6:sp r7:pc ---- 0484, 0000, 0000, 0000, 0000, 0000, 047e, 0018 jsr r5,006a ---- 0484, 0000, 0000, 0000, 0000, 001c, 047c, 006a mov r5,r0 ---- 001c, 0000, 0000, 0000, 0000, 001c, 047c, 006c mov sp,r5 ---- 001c, 0000, 0000, 0000, 0000, 047c, 047c, 006e mov r4,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 047a, 0070 mov r3,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0478, 0072 mov r2,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0476, 0074 tst -(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0474, 0076 2012/3/24 jmp (r0) 22
cret 関数の出口 main: cret: : mov r5,r1 jmp cret mov -(r1),r4 : mov -(r1),r3 mov -(r1),r2 mov r5,sp mov (sp)+,r5 rts pc r0 r1 r2 r3 r4 r5 r6:sp r7:pc ---- 0003, 0462, 0000, 0000, 0000, 047c, 046e, 003e jmp 0078 ---- 0003, 0462, 0000, 0000, 0000, 047c, 046e, 0078 mov r5,r1 ---- 0003, 047c, 0000, 0000, 0000, 047c, 046e, 007a mov -(r1),r4 -z-- 0003, 047a, 0000, 0000, 0000, 047c, 046e, 007c mov -(r1),r3 -z-- 0003, 0478, 0000, 0000, 0000, 047c, 046e, 007e mov -(r1),r2 -z-- 0003, 0476, 0000, 0000, 0000, 047c, 046e, 0080 mov r5,r6 ---- 0003, 0476, 0000, 0000, 0000, 047c, 047c, 0082 mov (r6)+,r5 -z-- 0003, 0476, 0000, 0000, 0000, 0000, 047e, 0084 rts 0084 2012/3/24 23
JSR – Jump to SubRoutine jsr src,dst 以下と等価 1. MOV src,-(R6) srcをスタックにpush 2. MOV PC,src 次の命令のPCをsrcに転送 3. JMP dst dstにジャンプ jsr r5,0x006aの処理は以下 1. r5の値をstackに入れて、spを-2 (push) 2. Jsrの次に実行する(戻り先)アドレスをr5に入 れて 3. PCにdstを入れてジャンプ 2012/3/24 24
RTS – ReTurn from Subroutine rts src 以下と等価 1. MOV src,PC 2. MOV (R6)+,src jsr r7の処理は以下 1. r7の値をPCに入れて(この場合は意味なし) 2. スタックの内容+2 (pop) してr7へ pcの書き換えによりジャンプ 2012/3/24 25
main 引数の設定とfuncの呼出し # r0 r1 r2 r3 r4 r5 r6 r7 15: 001c,0000,0000,0000,0000,047c,0474,001c sub $6,r6 main 16: 001c,0000,0000,0000,0000,047c,046e,0020 mov $1,-8(r5) main 17: 001c,0000,0000,0000,0000,047c,046e,0026 mov $2,-a(r5) main 18: 001c,0000,0000,0000,0000,047c,046e,002c mov -a(r5),(r6) main 19: 001c,0000,0000,0000,0000,047c,046e,0030 mov -8(r5),-(r6) main 20: 001c,0000,0000,0000,0000,047c,046c,0034 jsr r7,*$0x0040 main 21: 001c,0000,0000,0000,0000,047c,046a,0042 jsr r5,0x006a func #15 #16-17 #18 #19 #20 0x046a r6→ 0x0038 0x046c r6→ 1 1 0x046e r6→ r6→ r6→ 2 2 2 0x0470 0x0472 2 2 2 2 0x0474 r6→ 1 1 1 1 0x0476 0x0478 0x047a 0x047c r5→ r5→ r5→ r5→ r5→ 2012/3/24 26
funcの入り口 (func-csv) # r0 r1 r2 r3 r4 r5 r6 r7 21: 001c,0000,0000,0000,0000,047c,046a,0042 jsr r5,0x006a func 22: 001c,0000,0000,0000,0000,0046,0468,006a mov r5,r0 func-csv 23: 0046,0000,0000,0000,0000,0046,0468,006c mov r6,r5 func-csv 24: 0046,0000,0000,0000,0000,0468,0468,006e mov r4,-(r6) func-csv 25: 0046,0000,0000,0000,0000,0468,0466,0070 mov r3,-(r6) func-csv 26: 0046,0000,0000,0000,0000,0468,0464,0072 mov r2,-(r6) func-csv 27: 0046,0000,0000,0000,0000,0468,0462,0074 tst -(r6) func-csv 28: 0046,0000,0000,0000,0000,0468,0460,0076 jmp (r0) func #21 #23 #24-26 #27 0x045a 0x045c 0x045e 0x0460 r6→ 0x0462 r6→ r2 r2 0x0464 r3 r3 0x0466 r4 r4 0x0468 r6→ 0x047c r5,r6→ 0x047c r5→ 0x047c r5→ 0x047c 0x046a r6→ 0x0038 0x0038 0x0038 0x0038 0x046c 2012/3/24 27
funcの出口 (func-cret) # r0 r1 r2 r3 r4 r5 r6 r7 34:0003,0000,0000,0000,0000,0468,0460,005c jmp 0x00078 func 35:0003,0000,0000,0000,0000,0468,0460,0078 mov r5,r1 func-cret 36:0003,0468,0000,0000,0000,0468,0460,007a mov -(r1),r4 func-cret 37:0003,0466,0000,0000,0000,0468,0460,007c mov -(r1),r3 func-cret 38:0003,0464,0000,0000,0000,0468,0460,007e mov -(r1),r2 func-cret 39:0003,0462,0000,0000,0000,0468,0460,0080 mov r5,r6 func-cret 40:0003,0462,0000,0000,0000,0468,0468,0082 mov (r6)+,r5 func-cret 41:0003,0462,0000,0000,0000,047c,046a,0084 rts r7 func-cret 42:0003,0462,0000,0000,0000,047c,046c,0038 tst (r6)+ main #27-34 #35 #36-38 #39 #40-41 0x045a 0x045c 0x045e 0x0460 r6→ r6→ r6→ 0x0462 r2 r2 r1→ r2 0x0464 r3 r3 r3 0x0466 r4 r4 r4 0x0468 r5→ 0x047c r5,r1→ 0x047c r5→ 0x047c r5,r6→ 0x047c r6→ 0x047c 0x046a 0x0038 0x0038 0x0038 0x0038 r6→ 0x0038 0x046c r6→ 2012/3/24 28
関数とスタック  今のスタックr6:SP  前のスタックr5  r5:r6のペアでチェーン #20 #21 #27 0x0460 r6→ 0x0462 r2 0x0464 r3 0x0466 r4 0x0468 r6→ 0x047c r5→ 0x047c 0x046a 0x0038 0x0038 0x0038 0x046c 1 0x046e 2 0x0470 0x0472 2 0x0474 1 0x0476 0x0478 0x047a 0x047c r5→ 2012/3/24 29
レジスタ r0-r7 もう一度 r0, r1 演算結果の一時的な保持、復帰値など r2, r3, r4 関数内のローカル変数。呼ばれた関数で退避し、 関数の終了時に復元 R5 フレームポインタ r6 (sp) スタックポインタ r7 (pc) 実行する命令の位置(アドレス) 2012/3/24 30
C言語とasmの実行環境  asmから生成した命令列はスタックを必要と しない  C言語から生成した命令列は、実行時にス タックを必要とする  引数の受け渡し利用領域  自動変数の領域  呼び出し元レジスタ(r2,r3,r4)の退避領域  呼び出し先からの復帰アドレスの退避領域 2012/3/24 31
UNIXカーネルを理解するための 2012/3/24 32
getpid(2)の呼び出し C言語 main() { int i; i = getpid(); } 2012/3/24 33
getpid(2)の呼び出し ASM .globl _main .text _main: ~~main: ~i=177770 jsr r5,csv tst -(sp) jsr pc,_getpid mov r0,-10(r5) L1: jmp cret .globl .data 2012/3/24 34
getpid.s /usr/source/s4/getpid.s getpid = 20. .globl _getpid _getpid: mov r5,-(sp) mov sp,r5 sys getpid mov (sp)+,r5 rts pc 2012/3/24 35
getpid.sの実行トレース 3: 001c, 0000, 0000, 0000, 0000, 0446, 043c, 001e jsr r7, 0x00034 4: 001c, 0000, 0000, 0000, 0000, 0446, 043a, 0034 mov r5, -(r6) 5: 001c, 0000, 0000, 0000, 0000, 0446, 0438, 0036 mov r6,r5 6: 001c, 0000, 0000, 0000, 0000, 0438, 0438, 0038 sys getpid 7: 001c, 0000, 0000, 0000, 0000, 0438, 0438, 003a mov (r6)+, r5 8: 001c, 0000, 0000, 0000, 0000, 0446, 043a, 003c rts r7 なんか変じゃない 2012/3/24 36
open(2)の呼び出し C言語 main() { int f; f = open("hoge", 2); } 2012/3/24 37
open(2)の呼び出し ASM .globl _main .text _main: ~~main: ~f=177770 jsr r5,csv tst -(sp) mov $2,(sp) mov $L2,-(sp) jsr pc,*$_open tst (sp)+ mov r0,-10(r5) L1: jmp cret .globl .data L2:.byte 150,157,147,145,0 2012/3/24 38
open.s /usr/source/s5/open.s globl _open, cerror _open: mov r5,-(sp) mov sp,r5 mov 4(r5),0f mov 6(r5),0f+2 sys 0; 9f bec 1f jmp cerror 1: mov (sp)+,r5 rts pc .data 9: sys open; 0: ..; .. 2012/3/24 39
open.sの実行トレース # r0 r1 r2 r3 r4 r5 r6 r7 1: 001c, 0000, 0000, 0000, 0000, 0482, 0474, 0034 mov r5,-(r6) 2: 001c, 0000, 0000, 0000, 0000, 0482, 0472, 0036 mov r6,r5 3: 001c, 0000, 0000, 0000, 0000, 0472, 0472 0038 mov 4(r5), 0x008e 4: 001c, 0000, 0000, 0000, 0000, 0472, 0472, 003e mov 6(r5), 0x0090 5: 001c, 0000, 0000, 0000, 0000, 0472, 0472, 0044 sys indir 0x0008c 6: 0003, 0000, 0000, 0000, 0000, 0472, 0472, 0048 bcc 0x0004e 7: 0003, 0000, 0000, 0000, 0000, 0472, 0472, 004e mov (r6)+, r5 8: 0003, 0000, 0000, 0000, 0000, 0482, 0474, 0050 rts なんか変じゃない 2012/3/24 40
システムコールとライブラリコール  ライブラリ  マニュアルセクション3  例 fopen(3)  ユーザプログラムの空間で動作  必要に応じてシステムコールを呼び出す  システムコール  マニュアルセクション2  例 open(2)  OS内部の処理を呼び出す  呼出し方法は通常のCall/Returnとは異なる 2012/3/24 41
OSを実装するための 2012/3/24 42
Q:CPUが想定外の状態になると  たとえば以下の場合  不正なアドレスを参照した  未定義の命令を実行しようとした  0で割り算した  次の命令はどこから取り出せばよいか? 2012/3/24 43
A: わな “Trap”を用意しておく  CPUが身動きが取れなかった場合に次に命令 を取り出す場所を“わな”として仕掛けてお く  システムコールの呼び出しは特殊な「わな」  「わな」はCPUの処理過程に実行時の例外と して発生する  「わな」に引っかかる事を「例外」と呼ぶ 2012/3/24 44
例外と割り込み  Lions本9章  Trap/例外  命令の実行に失敗した場合の「わな」仕掛ける  システムコールの呼び出しは特殊な「わな」  「わな」はCPUの処理過程で発生する  Interrupt/割り込み  CPUの処理に関係なく、外部からの「割り込み」  主に、外部装置の処理完了時に「割り込む」  外部装置の例 ○ テレタイプ、紙テープ、ラインプリンタ、磁気ディ スク、時計 2012/3/24 45
2012/3/24 46
例外の流れ 1. 例外/割り込みの発生 2. コンテキストの保存 ① PCとPSWをCPU内部に一時的に保存 ② 発生の要因を特定し、要因:アドレスの表(ベクター)を 検索 ③ ベクターをPCに設定 ④ 一時的に保存していたPC,PSWをカーネルスタックに 退避(push) 3. ハンドラの実行 4. コンテキストの復元 ① カーネルスタックにPC,PSWが格納されている状態で rtt命令を実行 ② rtt命令がカーネルスタックからPC,PSWを復元(pop) 5. 割り込み発生の次の命令を実行 2012/3/24 47
例外ベクタ Vector Trap type Priority Location 004 Bus timeout 7 010 Illegal instruction 7 014 bpt-trace 7 020 iot 7 024 Power failure 7 030 Emulator trap 7 034 Trap instruction/ system entry 7 114 11/70 parity 7 240 Programmed interrupt 7 244 Floating point error 7 250 Segmentation violation 7 2012/3/24 48
500 0500 / low core 0501 0505 br7 = 340 0506 0507 . = 0^. 0508 br 1f 0509 4 0510 0511 / trap vectors 0512 trap; br7+0. / bus error 0513 trap; br7+1. / illegal instruction 0514 trap; br7+2. / bpt-trace trap 0515 trap; br7+3. / iot trap 0516 trap; br7+4. / power fail 0517 trap; br7+5. / emulator trap 0518 trap; br7+6. / system entry 2012/3/24 49
752 0752 .globl trap, call 0753 /* -------------- */ 0754 .globl _trap 0755 trap: 0756 mov PS,-4(sp) 0757 tst nofault 0758 bne 1f 0759 mov SSR0,ssr 0760 mov SSR2,ssr+4 0761 mov $1,SSR0 0762 jsr r0,call1; _trap 0763 / no return 2012/3/24 50
2693 2693 trap(dev, sp, r1, nps, r0, pc, ps) 2694 { 2695 register i, a; 2696 register struct sysent *callp; 2697 2698 savfp(); 2699 if ((ps&UMODE) == UMODE) 2700 dev =| USER; 2750 2702 switch(dev) { : 2715 default: 2716 printf("ka6 = %o¥n", *ka6); 2717 printf("aps = %o¥n", &ps); 2718 printf("trap type %o¥n", dev); 2719 panic("trap"); 2721 case 0+USER: /* bus error * 2722 i = SIGBUS; 2723 break; 2012/3/24 51
2751 2751 case 6+USER: /* sys call */ 2752 u.u_error = 0; 2753 ps =& ~EBIT; 2754 callp = &sysent[fuiword(pc-2)&077];  PCは”わな”に落ちた命令の次のアドレス  この命令は2バイトなので2を引いて  ユーザ空間からword(2バイト)を読み  下位6ビットを取り出す  この値をindexにしてsysentを見ると 2012/3/24 52
2906 2906 * to the appropriate routine for processing a system call. 2907 * Each row contains the number of arguments 2908 * and a pointer to the routine. 2909 */ 2910 int sysent[] 2911 { 2912 0, &nullsys, /* 0 = indir */ 2913 0, &rexit, /* 1 = exit */ 2914 0, &fork, /* 2 = fork */ 2915 2, &read, /* 3 = read */ 2916 2, &write, /* 4 = write */ 2917 2, &open, /* 5 = open */ 2012/3/24 53
システムコール番号 indir = 0. exit = 1. fork = 2. read = 3. write = 4. open = 5. close = 6. wait = 7. creat = 8. : : 2012/3/24 54
システムコール呼出し処理 1. C言語で open(“aa”, 2) 2. Libcで sys 5 を実行  sysはemulator trap instruction 3. ユーザプログラム空間から5を取り出して 4. sysent[5]でカーネル内のopen処理を見つけ 5. カーネル内部のopen処理を呼ぶ 2012/3/24 55
2012/3/24 56
割り込みベクタ Vector device priority Location 060 Teletype input 4 064 Teletype output 4 070 Paper tape input 4 074 Paper tape output 4 100 Line clock 6 104 Programmable clock 6 200 Line printer 4 220 RK disk driver 5 2012/3/24 57
Teletype ? 2012/3/24 58
Paper tape ? 2012/3/24 59
Clocks – line and programmable  Line Clock  電源の周期から生成  AC>降圧(トランス)>整流(ダイオード)>コンデンサ  電源周波数のパルスを取り出せる  50HZで20ms間隔  昔の電気式デジタル時計はパルスxHZで1秒を生成  Programmable clock  指定の間隔でパルスを発生  PDP-11ではどちらかが必要 2012/3/24 60
525 low.s 0525: . = 60^. Vector device entry 0526: klin; br4 0527: klou; br4 060 Teletype input klin 0528: 0529: . = 70^. 064 Teletype output klou 0530: pcin; br4 0531: pcou; br4 070 Paper tape input pcin 0532: 074 Paper tape output pcou 0533: . = 100^. 0534: kwlp; br6 100 Line clock kwlp 0535: kwlp; br6 0539: 104 Programmable clock kwlp 0540: . = 200^. 0541: lpou; br4 200 Line printer lpou 0542: 0543: . = 220^. 220 RK disk driver rkio 0544: rkio; br5 2012/3/24 61
Line Clock  外部にあるclockから毎秒50個のパルスがCPUに 入る  1パルス毎に割り込みベクタの処理を実行 Clock CPU 2012/3/24 62
kwlp low.s 0568: 0569: .globl _clock 0570: kwlp: jsr r0,call; _clock Cで書かれたclock()を呼ぶ 2012/3/24 63
clock 3725: clock(dev, sp, r1, nps, r0, pc, ps) 3761: 3797: if(++lbolt >= HZ) { 3726: { 3762: /* 3798: if((ps&0340) != 0) 3727: register struct callo *p1, *p2; 3763: * callout 3799: return; 3728: register struct proc *pp; 3764: */ 3800: lbolt =- HZ; 3729: 3765: 3801: if(++time[1] == 0) 3730: /* 3766: spl5(); 3802: ++time[0]; 3731: * restart clock 3767: if(callout[0].c_time <= 0) { 3803: spl1(); 3732: */ 3768: p1 = &callout[0]; 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3733: 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3805: wakeup(tout); 3734: *lks = 0115; 3770: (*p1->c_func)(p1->c_arg); 3806: if((time[1]&03) == 0) { 3735: 3771: p1++; 3807: runrun++; 3736: /* 3772: } 3808: wakeup(&lbolt); 3737: * display register 3773: p2 = &callout[0]; 3809: } 3738: */ 3774: while(p2->c_func = p1->c_func) { 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) 3739: 3775: p2->c_time = p1->c_time; 3811: if (pp->p_stat) { 3740: display(); 3776: p2->c_arg = p1->c_arg; 3812: if(pp->p_time != 127) 3741: 3777: p1++; 3813: pp->p_time++; 3742: /* 3778: p2++; 3814: if((pp->p_cpu & 0377) > SCHMAG) 3743: * callouts 3779: } 3815: pp->p_cpu =- SCHMAG; else 3744: * if none, just return 3780: } 3816: pp->p_cpu = 0; 3745: * else update first non-zero time 3781: 3817: if(pp->p_pri > PUSER) 3746: */ 3782: /* 3818: setpri(pp); 3747: 3783: * lightning bolt time-out 3819: } 3748: if(callout[0].c_func == 0) 3784: * and time of day 3820: if(runin!=0) { 3749: goto out; 3785: */ 3821: runin = 0; 3750: p2 = &callout[0]; 3786: 3822: wakeup(&runin); 3751: while(p2->c_time<=0 && p2->c_func!=0) 3787: out: 3823: } 3752: p2++; 3788: if((ps&UMODE) == UMODE) { 3824: if((ps&UMODE) == UMODE) { 3753: p2->c_time--; 3789: u.u_utime++; 3825: u.u_ar0 = &r0; 3754: 3790: if(u.u_prof[3]) 3826: if(issig()) 3755: /* 3791: incupc(pc, u.u_prof); 3827: psig(); 3756: * if ps is high, just return 3792: } else 3828: setpri(u.u_procp); 3757: */ 3793: u.u_stime++; 3829: } 3758: 3794: pp = u.u_procp; 3830: } 3759: if((ps&0340) != 0) 3795: if(++pp->p_cpu == 0) 3831: } 3760: goto out; 3796: pp->p_cpu--; 2012/3/24 64
clock 3725: clock(dev, sp, r1, nps, r0, pc, ps) 3761: 3797: if(++lbolt >= HZ) { 3726: { 3762: /* 3798: if((ps&0340) != 0) 3727: register struct callo *p1, *p2; 3763: * callout 3799: return; 3728: register struct proc *pp; 3764: */ 3800: lbolt =- HZ; 3729: 3765: 3801: if(++time[1] == 0) 3730: /* 3766: spl5(); 3802: ++time[0]; 3731: * restart clock 3767: if(callout[0].c_time <= 0) { 3803: spl1(); 3732: */ 3768: p1 = &callout[0]; 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3733: 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3805: wakeup(tout); 3734: *lks = 0115; 3770: (*p1->c_func)(p1->c_arg); 3806: if((time[1]&03) == 0) { 3735: 3771: p1++; 3807: runrun++; Callout 3736: /* 3772: } 3808: wakeup(&lbolt); 3737: * display register 3773: p2 = &callout[0]; 3809: } 3738: */ 3774: while(p2->c_func = p1->c_func) { 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) 3739: 3775: p2->c_time = p1->c_time; 3811: if (pp->p_stat) { Sched 3740: display(); 3776: p2->c_arg = p1->c_arg; 3812: if(pp->p_time != 127) 3741: 3777: p1++; 3813: pp->p_time++; 3742: /* 3778: p2++; 3814: if((pp->p_cpu & 0377) > SCHMAG) 3743: * callouts 3779: } 3815: pp->p_cpu =- SCHMAG; else 3744: * if none, just return 3780: } 3816: pp->p_cpu = 0; 3745: * else update first non-zero time 3781: 3817: if(pp->p_pri > PUSER) の入り口 3746: */ 3782: /* 3818: setpri(pp); 3747: 3783: * lightning bolt time-out 3819: } 3748: if(callout[0].c_func == 0) 3784: * and time of day 3820: if(runin!=0) { 3749: goto out; 3785: */ 3821: runin = 0; 3750: p2 = &callout[0]; 3786: 3822: wakeup(&runin); Acct 3751: while(p2->c_time<=0 && p2->c_func!=0) 3787: out: 3823: } 3752: p2++; 3788: if((ps&UMODE) == UMODE) { 3824: if((ps&UMODE) == UMODE) { 3753: p2->c_time--; 3789: u.u_utime++; 3825: u.u_ar0 = &r0; 3754: 3790: if(u.u_prof[3]) 3826: if(issig()) 3755: /* 3791: incupc(pc, u.u_prof); 3827: psig(); 3756: * if ps is high, just return 3792: } else 3828: setpri(u.u_procp); 3757: */ 3793: u.u_stime++; 3829: } 3758: 3794: pp = u.u_procp; 3830: } 3759: if((ps&0340) != 0) 3795: if(++pp->p_cpu == 0) 3831: } 3760: goto out; 3796: pp->p_cpu--; 2012/3/24 65
謎の変数  u.u_time  u.u_stime  pp->p_time  lbolt  time[2]  tout[2] 2012/3/24 66
callout 構造 struct callo { int c_time; /* ticks between events */ int c_arg; /* function argument */ int (*c_func)(); /* function pointer */ } callout[NCALL]; 2012/3/24 67
clock callout (1/2) 先頭のcalloutが未 3748: if(callout[0].c_func == 0) 登録なら、後続の 3749: goto out; 3750: p2 = &callout[0]; calloutも未登録と 判断して,Callout 処理をスキップ 3751: while(p2->c_time<=0 && p2->c_func!=0) 3752: p2++; 最初にc_timeが0 3753: p2->c_time--; より大きいCallout 3758: を見つけて、 3759: if((ps&0340) != 0) c_timeを-1 3760: goto out; ここより前の 3761: calloutはこの後処 理される 2012/3/24 68
clock callout (2/2) 先頭に登録時間が 過ぎているcallout 3766: spl5(); が存在したら、 3767: if(callout[0].c_time <= 0) { 3768: p1 = &callout[0]; 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3770: (*p1->c_func)(p1->c_arg); 3771: p1++; 処理を実行 3772: } 3773: p2 = &callout[0]; 3774: while(p2->c_func = p1->c_func) { 3775: p2->c_time = p1->c_time; Callout配列を詰め 3776: p2->c_arg = p1->c_arg; る 3777: p1++; 3778: p2++; 3779: } 3780: } 2012/3/24 69
clock acct USERモードから 3787: out: 割り込まれたら、 3788: if((ps&UMODE) == UMODE) { uのuser時間 3789: u.u_utime++; u_utimeを+1 3790: if(u.u_prof[3]) 3791: incupc(pc, u.u_prof); KERNELモードか 3792: } else ら割り込まれたら、 3793: u.u_stime++; uのsystem時間 u_stimeを+1 3794: pp = u.u_procp; 3795: if(++pp->p_cpu == 0) Procのp_cpuを加 3796: pp->p_cpu--; 算。オーバフロー しないように調整 ppはproc構造のポインタ、uはuser構造。共 にプロセスの管理構造1プロセスに1個づつ 2012/3/24 70
clock schedの入り口(1/2) clockが呼ばれると、ここまでは必 3797: if(++lbolt >= HZ) { ず実行。lboltを+1して、HZ(50)を 3798: if((ps&0340) != 0) 3799: return; 超過したら、すなわち1秒経過し 3800: lbolt =- HZ; たら、 3801: if(++time[1] == 0) 3802: ++time[0]; lboltをリセット 3803: spl1(); 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3805: wakeup(tout); 寝ているプロセスを起こす 3806: if((time[1]&03) == 0) { 3807: runrun++; 3808: wakeup(&lbolt); 3809: } 4秒ごとにlboltで待ってるプロセ スを起こす 今回はさらりと流す 2012/3/24 71
clock schedの入り口(2/2) 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) プロセスの管理構造の配 3811: if (pp->p_stat) { 列をスキャン 3812: if(pp->p_time != 127) 3813: pp->p_time++; 3814: if((pp->p_cpu & 0377) > SCHMAG) p_statに値が入っている=プロ 3815: pp->p_cpu =- SCHMAG; elseセスが存在したら、p_timeを 3816: pp->p_cpu = 0; +1 3817: if(pp->p_pri > PUSER) 3818: setpri(pp); 3819: } 3820: if(runin!=0) { 3821: runin = 0; 3822: wakeup(&runin); runinが真なら、runinで待って 3823: } るプロセスをwakeup 3824: if((ps&UMODE) == UMODE) { 3825: u.u_ar0 = &r0; 3826: if(issig()) 3827: psig(); 3828: setpri(u.u_procp); 3829: } 3830: 2012/3/24 } 72
謎の変数 回答  u.u_time USER時間  u.u_stime SYSTEM時間  pp->p_time プロセス起動からの時間  lbolt 毎秒60回++  time[2] 1970年からの秒  tout[2] sleep(2)が起きる時間 2012/3/24 73
Unix のPPDA (PerProcDataArea)  User構造体 user.h The user structure. One allocated per process. Contains all per process data that doesn't need to be referenced while the process is swapped.  Proc構造体 proc.h One structure allocated per active process. It contains all data needed about the process while the process may be swapped out. Other per process data (user.h) is swapped with the process. 2012/3/24 74
2012/3/24 75
a.outと空間の関係 CPU PC a.out header text text data data exec(2) bss ・関数を呼 び出すと自 3076 /* read in first 8 bytes 動的に割り 3077 * of file for segment 当てる。 3078 * sizes: ・呼び出し 3079 * w0 = 407/410/411 3080 * w1 = text size からの復帰 3081 * w2 = data size に備えてレ 3082 * w3 = bss size stack ジスタの内 3083 */ 容を退避。 2012/3/24 76
例外/割り込みが起こると…….. •PSWのモードがユーザモー CPU PC ドからカーネルモードに変わ り、空間が切り替わる。 ① ② •事象に応じて、ベクター テーブルの値がPCに書き込 text ③ text A まれる。 data data •例外/割り込み発生時のPSW とPC(図中A)をスタックに pushする bss bss アプリケーション カーネル アプリケーションと stack stack カーネル 2つの空間?? 2012/3/24 77
仮想アドレスと物理アドレス  現在の一般的な仮想記憶  仮想アドレス>物理アドレス  仮想メモリ量>物理メモリ量  実メモリ不足時は固定長(ページ)単位で2次記憶に退避 し必要時に復元 →ページング  PDP-11の仮想記憶  仮想アドレス<物理アドレス  仮想メモリ量<物理メモリ量  実メモリ不足時はプロセス全体を2次記憶に退避し実行 を抑止、一定時間経過後に全体を復元し実行可能に →スワッピング 2012/3/24 78
仮想記憶のための仕組み  ハードウェアのサポートが必要  仮想アドレス→物理アドレスの変換機構 CPU memory 変換機構なし CPU 変換表 memory 変換機構あり 2012/3/24 79
アドレス変換機構の制御  リセット後は変換機構OFFで動作  OSの初期化時にプログラムで 1. 変換表を初期化 2. 変換機構をON  新しいプロセスの生成とメモリ獲得時に  OSが変換表を編集  プロセスの切り替え時に  変換表を入れ替え 2012/3/24 80
PDP-11/40のメモリ管理機構  8個のカーネルモードページ  8個のユーザモードページ  1ページは最大8Kbyteの可変長  モード切り替えはPSWのモードに連動 2012/3/24 81
Active Page Registers (APR) 2012/3/24 82
APR- PARとPDRのペア PAR ベースアドレスを指定 PDR メモリ属性を指定 2012/3/24 83
仮想記憶の有効化  bit0 ENABLE MANAGEMENTを1で有効 2012/3/24 84
重要なアドレス m40.s USIZE = 16 Size of User Block (*64 = 1024 B) PS = 177776 Program Status Word SSR0 = 177572 Status Register KISA0 = 172340 Kernel Segment Address Register #0 KISA6 = 172354 Kernel Segment Address Register #6 KISD0 = 172300 Kernel Segment Descriptor Register #0 UISA0 = 177640 User Segment Address Register #0 UISA1 = 177642 User Segment Address Register #1 UISD0 = 177600 User Segment Descriptor Register #0 UISD1 = 177602 User Segment Descriptor Register #1 IO = 7600 I/O Segment Register 2012/3/24 85
start 仮想空間の設定 0612: start: 0613: bit $1,SSR0 0614: bne start / loop if restart 0615: reset 0616: 0617: / initialize systems segments 0618: 0619: mov $KISA0,r0 0620: mov $KISD0,r1 0621: mov $200,r4 0622: clr r2 0623: mov $6,r3 0624: 1: 0625: mov r2,(r0)+ 0626: mov $77406,(r1)+ / 4k rw 0627: add r4,r2 0628: sob r3,1b 2012/3/24 86
ユーザ空間の初期化 start 0630: / initialize user segment 0631: 0632: mov $_end+63.,r2 0633: ash $-6,r2 0634: bic $!1777,r2 0635: mov r2,(r0)+ / ksr6 = sysu 0636: mov $usize-1¥<8|6,(r1)+ 0637: 0638: / initialize io segment 0639: / set up counts on supervisor segments 0640: 0641: mov $IO,(r0)+ 0642: mov $77406,(r1)+ / rw 4k 0643: 0644: / get a sp and start segmentation 0645: 0646: mov $_u+[usize*64.],sp 0647: inc SSR0 2012/3/24 87
bssとuのクリア 0646: mov $_u+[usize*64.],sp 0647: inc SSR0 0648: 0649: / clear bss 0650: 0651: mov $_edata,r0 0652: 1: 0653: clr (r0)+ 0654: cmp r0,$_end 0655: blo 1b 0656: 0657: / clear user block 0658: 0659: mov $_u,r0 0660: 1: 0661: clr (r0)+ 0662: cmp r0,$_u+[usize*64.] 0663: blo 1b 2012/3/24 88
mainの呼び出し 0665: / set up previous mode and call main 0666: / on return, enter user mode at 0R 0667: 0668: mov $30000,PS 0669: jsr pc,_main 0670: mov $170000,-(sp) 0671: clr -(sp) 0672: rtt 2012/3/24 89
最初のプロセスを生成 1627: if(newproc()) { 1628: expand(USIZE+1); 1629: estabur(0, 1, 0, 0); 1630: copyout(icode, 0, sizeof icode); 1631: /* 1632: * Return goes to loc. 0 of user init 1633: * code just copied out. 1634: */ 1635: return; 1636: } 1637: sched(); 2012/3/24 90
icode 1511: /* 1512: * Icode is the octal bootstrap 1513: * program executed in user mode 1514: * to bring up the system. 1515: */ 1516: int icode[] 1517: { 1518: 0104413, /* sys exec; init; initp */ 1519: 0000014, 1520: 0000010, char* init = “/etc/init”; 1521: 0000777, /* br . */ 1522: 0000014, /* initp: init; 0 */ main( ) { 1523: 0000000, execl( init, init, 0 ); 1524: 0062457, /* init: </etc/init¥0> */ while( 1 ) ; 1525: 0061564, 1526: 0064457, } 1527: 0064556, 1528: 0000164, 1529: }; 2012/3/24 91
Bootと空間の初期化 Boot直後 仮想記憶有効 最初のnewproc 最初のexec Initの実行 text text text text text data data data data data bss bss bss bss u user user user user user user IOレジスタ IOレジスタ IOレジスタ IOレジスタ icode exec text data bss stack 2012/3/24 92
Unixの空間モデル  アプリとカーネルのアドレス域は分離  後に完全な仮想記憶サポートで少し変わる  各アプリケーションの空間は独立  カーネルの空間は  Text,Data,Bssは一部を除いて共有  Stackはアプリ毎に個別  Stackはstruct userのページの後半 2012/3/24 93
UNIXカーネルの構造 2012/3/24 94
PPDA - Per Processor Data Area  OSを読み解く場合に最初に注目する構造  プロセスなどの実行単位毎に割り当てる領域  UNIXの場合はuserとproc 2012/3/24 95
struct user 0413 struct user 0414 { 0415 int u_rsav[2]; /* save r5,r6 when exchanging stacks */ 0416 int u_fsav[25]; /* save fp registers */ 0417 /* rsav and fsav must be first in structure */ 0418 char u_segflg; /* flag for IO; user or kernel space */ 0419 char u_error; /* return error code */ 0420 char u_uid; /* effective user id */ 0421 char u_gid; /* effective group id */ 0422 char u_ruid; /* real user id */ 0423 char u_rgid; /* real group id */ 0424 int u_procp; /* pointer to proc structure */ 0436 int u_uisa[16]; /* prototype of segmentation addresses */ 043 int u_uisd[16]; /* prototype of segmentation descriptors */ 2012/3/24 96
struct proc 0358 struct proc 0359 { 0360 char p_stat; 0361 char p_flag; 0362 char p_pri; /* priority, negative is high */ 0363 char p_sig; /* signal number sent to this process */ 0364 char p_uid; /* user id, used to direct tty signals */ 0365 char p_time; /* resident time for scheduling */ 0366 char p_cpu; /* cpu usage for scheduling */ 0367 char p_nice; /* nice for scheduling */ 0368 int p_ttyp; /* controlling tty */ 0369 int p_pid; /* unique process id */ 0370 int p_ppid; /* process id of parent */ 0371 int p_addr; /* address of swappable image */ 0372 int p_size; /* size of swappable image (*64 bytes) */ 0373 int p_wchan; /* event process is awaiting */ 0374 int *p_textp; /* pointer to text structure */ 0376 } proc[NPROC]; 2012/3/24 97
空間の切り替え機構 CPU PC アプリケーション カーネル PSW 00: カーネルモード text 11: ユーザモード text data 11用 00用 data APR0 APR0 APR1 APR1 bss APR2 APR2 bss APR3 APR3 APR4 APR4 APR5 APR5 APR6 APR6 struct user stack APR7 APR7 stack I/Oレジスタ 2012/3/24 98
アプリケーションの動作時 PSW PC •PSWのモードはユーザ •PCはアプリのtextセグメントを指す text data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 99
割り込みの発生-空間の切り替え PSW PC •PSWのモードがカーネルに •アドレス空間の制御レジスタ text がカーネルモードに切り替わる data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 100
割り込みの発生-割り込みベクタ PSW PC •割り込みベクタのエントリを text 選択してPCに data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss •1のアドレスとpswをスタック に退避 stack stack stack stack 2012/3/24 101
割り込みの発生-割り込みベクタ PSW PC •割り込みベクタのエントリを text 選択してPCに data bss Vector device 004 Bus Timeout user user Illegal instruction 010 user user kstack kstackBpt-trace kstack 014 kstack 024 iot Vector device entry IOレジスタ 034 Power failur 060 Teletype input klin 114 Emulator tarp 064 Teletype output klou instruction 1 text text text 070 text Paper tape input pcin 240 11/10 parity data data data 074 data Paper tape output pcou 244 Floting point error bss bss bss 100 bss Line clock kwlp 250 Segmentation violation 104 Programmable clock kwlp stack stack stack200 stack Line printer lpou 220 RK disk driver rkio 2012/3/24 102
割り込みの発生ーハンドラの処理 PSW PC 2 text data bss user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 103
割り込みのからの復帰 PSW PC カーネル用 APR0 text APR1 data APR2 APR3 bss APR4 APR5 APR6 user user user user APR7 kstack kstack kstack kstack IOレジスタ アプリ用 •処理が終わったら、スタックのPCと 1 text text PSW復元して割り込み時点に復帰 text text APR0 APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 104
 以上の説明は、割り込み発生時から、復帰ま でにプロセスの切り替えが発生しない場合  条件によっては以下の処理でプロセスの切り 替えが発生 1. procの検索と選択 2. user構造の切り替え 3. User構造+kstackに退避した状態の復元 4. アプリへの復帰 2012/3/24 105
プロセスの切り替えー選択 PSW PC Procの検索(スケジューラ) text data ↑次に動かすアプリ(プロセス) bss のprocを見つける user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 106
プロセスの切り替えーuserの切り替え PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 107
プロセスの切り替えーuserの切り替え PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 空間管理用のレジスタを更新 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 108
プロセスの切り替えーモードの切り替え •PSWをユーザに切り替え PSW PC •PCを復元 •退避していた、PCとレジスタを復元 text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 109
プロセスの切り替えー完了 PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 110
swtch 1/3 2178: swtch() 2179: { 2180: static struct proc *p; 2181: register i, n; 2182: register struct proc *rp; 2183: 2184: if(p == NULL) 2185: p = &proc[0]; 2186: /* 2187: * Remember stack of caller 2188: */ 2189: savu(u.u_rsav); /* save r5,r6 when exchanging stacks */ 2190: /* 2191: * Switch to scheduler's stack 2192: */ 2193: retu(proc[0].p_addr); /* address of swappable image */ 2012/3/24 111
swtch 2/3 2195: loop: 2196: runrun = 0; 2197: rp = p; 2198: p = NULL; 2199: n = 128; 2203: i = NPROC; 2204: do { 2205: rp++; 2206: if(rp >= &proc[NPROC]) 2207: rp = &proc[0]; 2208: if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)!=0) { 2209: if(rp->p_pri < n) { 2210: p = rp; 2211: n = rp->p_pri; 2212: } 2213: } 2214: } while(--i); 2012/3/24 112
swtch 3/3 2223: rp = p; 2224: curpri = n; 2225: /* Switch to stack of the new process and set up 2226: * his segmentation registers. 2227: */ 2228: retu(rp->p_addr); /* address of swappable image */ 2229: sureg(); 2247: return(1); 2012/3/24 113
あやしい関数 2189: savu(u.u_rsav); 今のプロセスのr5,r6をuに退避 2193: retu(proc[0].p_addr); uをproc0に 2228: retu(rp->p_addr); uを切り替えるプロセスに 2229: sureg(); uに退避してあるAPRをハードのARPに設定 2012/3/24 114
savu 0725: _savu: 0726: bis $340,PS 0727: mov (sp)+,r1 0728: mov (sp),r0 0729: mov sp,(r0)+ 0730: mov r5,(r0)+ bis $340,PSでbit5-7を1に bic $340,PSでbit5-7を0に 0731: bic $340,PS 0732: jmp (r1) 725: 727-728: 729-730: 732: r0 u.u_rsav r1 戻り先 r6→ 戻り先 戻り先 u. u.u_rsav r6→ u.u_rsav sp r5 r1 戻り先 2012/3/24 115
retu 0740: _retu: 0745: 1: 0741: bis $340,PS 0746: mov (r0)+,sp 0742: mov (sp)+,r1 0747: mov (r0)+,r5 0743: mov (sp),KISA6 0748: bic $340,PS 0744: mov $_u,r0 0749: jmp (r1) 0413 struct user 0414 { 0415 int u_rsav[2]/* save r5,r6 when exchanging stacks */ 742: 743-744: 746-747: u. sp r6→ 戻り先 戻り先 r5 r6→ p_addr r6→ p_addr カーネル用 r6→ p_addr APR0 APR1 APR2 APR3 APR4 戻り先 r1 戻り先 APR5 p_addr r1 戻り先 r6 sp r0 u.u_rsav APR7 r0 u.u_rsav r5 r5 2012/3/24 116
sureg 1739: sureg() 1747: up =- 8; 1740: { 1748: rp =- 8; 1741: register *up, *rp, a; 1749: } 1742: 1750: while(rp > &UISA->r[0]) 1743: a = u.u_procp->p_addr; 1751: *--rp = *--up + a; 1744: up = &u.u_uisa[16]; 1754: up = &u.u_uisd[16]; 1745: rp = &UISA->r[16]; 1755: rp = &UISD->r[16]; 1746: if(cputype == 40) { 1765: } アプリ用 APR0 u.u_uisa[0-7] APR1 APR2 APR3 u.u_uisd[0-7] APR4 アプリの空間を設定 APR5 APR6 APR7 2012/3/24 117
2012/3/24 118
勉強会の記録  日本Androidの会 沖縄支部 workshop@Naha Vol.17  http://atnd.org/events/25533  http://togetter.com/li/278270  横浜Androidプラットフォーム第19回勉強会  http://atnd.org/events/26444  http://togetter.com/li/281856 2012/3/24 119
付録2 よくわかんない人のための 2012/3/24 120
Simhのインストールと起動  Ubuntuの場合 $ sudo apt-get install simh  pdp-11のシュミレーション $ pdp11 PDP-11 simulator V3.8-1 sim> 2012/3/24 121
Unix v6のダウンロード  simh用Diskイメージのアーカイブ  http://simh.trailing-edge.com/software.html  V6のイメージは以下  http://simh.trailing-edge.com/kits/uv6swre.zip  ダウンロード $ wget http://simh.trailing-edge.com/kits/uv6swre.zip 2012/3/24 122
Diskイメージの展開 $ unzip uv6swre.zip Archive: uv6swre.zip inflating: README.txt inflating: unix3_v6_rk.dsk inflating: unix1_v6_rk.dsk inflating: unix2_v6_rk.dsk inflating: unix0_v6_rk.dsk inflating: AncientUnix.pdf $ 2012/3/24 123
設定ファイルの作成  uv6swre.zipを展開したディレクトリで以下の ファイルunixv6.cfを作成 $cat unixv6.cfg set cpu 11/40 set cpu u18 att rk0 unix0_v6_rk.dsk att rk1 unix1_v6_rk.dsk att rk2 unix2_v6_rk.dsk att rk3 unix3_v6_rk.dsk boot rk0 $ 2012/3/24 124
Simhの起動 $ pdp11 unixv6.cfg PDP-11 simulator V3.8-1 Disabling XQ @unix Login: root # 2012/3/24 125
Simhのdebug機能  simhモードへの移行と復帰 # Ctrl+E Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> c #  simhモードへはCtrl+Eで  シミュレーションモードへはcで 2012/3/24 126
ディバック機能を使う  カーネルのシンボルアドレスを調べる # chdir / # nm unix | grep savu 021636T _savu #  savu()のアドレスは021636, breakpointを指定する # Ctrl+E Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> break 021636 sim> c Breakpoint, PC: 021636 (SPL 6) savu()で止まった sim> 2012/3/24 127
step実行 Breakpoint, PC: 021636 (SPL 6) sim> e r5,sp,pc R5: 141742 SP: 141730 PC: 021636 sim> s 0725: _savu: Step expired, PC: 021640 (MOV (SP)+,R1) sim> s 0726: bis $340,PS Step expired, PC: 021642 (MOV (SP),R0) sim> s 0727: mov (sp)+,r1 Step expired, PC: 021644 (MOV SP,(R0)+) sim> s 0728: mov (sp),r0 Step expired, PC: 021646 (MOV R5,(R0)+) 0729: mov sp,(r0)+ sim> s 0730: mov r5,(r0)+ Step expired, PC: 021650 (SPL 0) sim> s 0731: bic $340,PS Step expired, PC: 021652 (JMP (R1)) 0732: jmp (r1) 2012/3/24 128
レジスタの調べ方 /usr/share/doc/simh/simh_doc.pdfで説明 e {xamine} <list> examine memory or registers sim> e state // レジスタ等すべてを表示 PC: 021630 V: 0 KIPDR4: 077406 SIPDR4: 000000 UIPDR4: 000000 R0: 140004 C: 0 KIPAR5: 001416 SIPAR5: 000000 UIPAR5: 000000 R1: 034272 PIRQ: 000000 KIPDR5: 077406 SIPDR5: 000000 UIPDR5: 000000 R2: 005336 STKLIM: 000000 KIPAR6: 001616 SIPAR6: 000000 UIPAR6: 000000 R3: 000200 FAC0H: 00000000000 KIPDR6: 077406 SIPDR6: 000000 UIPDR6: 000000 R4: 000000 FAC0L: 00000000000 KIPAR7: 007600 SIPAR7: 000000 UIPAR7: 001541 R5: 141724 FAC1H: 00000000000 KIPDR7: 077506 SIPDR7: 000000 UIPDR7: 066016 SP: 141710 FAC1L: 00000000000 KDPAR0: 000000 SDPAR0: 000000 UDPAR0: 001714 R00: 140004 FAC2H: 00000000000 KDPDR0: 077506 SDPDR0: 000000 UDPDR0: 000006 R01: 034272 FAC2L: 00000000000 KDPAR1: 000200 SDPAR1: 000000 UDPAR1: 000000 R02: 005336 FAC3H: 00000000000 KDPDR1: 077506 SDPDR1: 000000 UDPDR1: 000000 R03: 000200 FAC3L: 00000000000 KDPAR2: 000400 SDPAR2: 000000 UDPAR2: 000000 R04: 000000 FAC4H: 00000000000 KDPDR2: 077506 SDPDR2: 000000 UDPDR2: 000000 R05: 141724 FAC4L: 00000000000 KDPAR3: 000600 SDPAR3: 000000 UDPAR3: 000000 R10: 000000 FAC5H: 00000000000 KDPDR3: 077406 SDPDR3: 000000 UDPDR3: 000000 R11: 000000 FAC5L: 00000000000 KDPAR4: 001000 SDPAR4: 000000 UDPAR4: 000000 R12: 000000 FPS: 000004 KDPDR4: 077406 SDPDR4: 000000 UDPDR4: 000000 R13: 000000 FEA: 000000 KDPAR5: 001200 SDPAR5: 000000 UDPAR5: 000000 R14: 000000 FEC: 00 KDPDR5: 077406 SDPDR5: 000000 UDPDR5: 000000 R15: 000000 MMR0: 000201 KDPAR6: 001171 SDPAR6: 000000 UDPAR6: 000000 KSP: 141710 MMR1: 000000 KDPDR6: 077506 SDPDR6: 000000 UDPDR6: 000000 SSP: 000000 MMR2: 021626 KDPAR7: 007600 SDPAR7: 000000 UDPAR7: 001541 USP: 177756 MMR3: 000005 KDPDR7: 077506 SDPDR7: 000000 UDPDR7: 066016 PSW: 030000 KIPAR0: 000000 SIPAR0: 003612 UIPAR0: 001714 IREQ[0]: 00000000000 CM: 0 KIPDR0: 077406 SIPDR0: 000106 UIPDR0: 000006 TRAPS: 00000 PM: 3 KIPAR1: 000416 SIPAR1: 003700 UIPAR1: 000000 WAIT: 1 RS: 0 KIPDR1: 077506 SIPDR1: 000106 UIPDR1: 000000 STOP_TRAPS: 00001 FPD: 0 KIPAR2: 000616 SIPAR2: 000000 UIPAR2: 000000 STOP_VECA: 1 IPL: 0 KIPDR2: 077506 SIPDR2: 000000 UIPDR2: 000000 STOP_SPA: 1 T: 0 KIPAR3: 001016 SIPAR3: 000000 UIPAR3: 000000 PCQ[0]: 034412 N: 0 KIPDR3: 077506 SIPDR3: 000000 UIPDR3: 000000 WRU: 005 Z: 0 KIPAR4: 001216 SIPAR4: 000000 UIPAR4: 000000 2012/3/24 129
必要な内容に絞り込む PC,R0~R5,SP(KSP,USP) PSW KDPAR0~7 UDPAR0~7 PC: 021630 V: 0 KIPDR4: 077406 SIPDR4: 000000 UIPDR4: 000000 R0: 140004 C: 0 KIPAR5: 001416 SIPAR5: 000000 UIPAR5: 000000 R1: 034272 PIRQ: 000000 KIPDR5: 077406 SIPDR5: 000000 UIPDR5: 000000 R2: 005336 STKLIM: 000000 KIPAR6: 001616 SIPAR6: 000000 UIPAR6: 000000 R3: 000200 FAC0H: 00000000000 KIPDR6: 077406 SIPDR6: 000000 UIPDR6: 000000 R4: 000000 FAC0L: 00000000000 KIPAR7: 007600 SIPAR7: 000000 UIPAR7: 001541 R5: 141724 FAC1H: 00000000000 KIPDR7: 077506 SIPDR7: 000000 UIPDR7: 066016 SP: 141710 FAC1L: 00000000000 KDPAR0: 000000 SDPAR0: 000000 UDPAR0: 001714 R00: 140004 FAC2H: 00000000000 KDPDR0: 077506 SDPDR0: 000000 UDPDR0: 000006 R01: 034272 FAC2L: 00000000000 KDPAR1: 000200 SDPAR1: 000000 UDPAR1: 000000 R02: 005336 FAC3H: 00000000000 KDPDR1: 077506 SDPDR1: 000000 UDPDR1: 000000 R03: 000200 FAC3L: 00000000000 KDPAR2: 000400 SDPAR2: 000000 UDPAR2: 000000 R04: 000000 FAC4H: 00000000000 KDPDR2: 077506 SDPDR2: 000000 UDPDR2: 000000 R05: 141724 FAC4L: 00000000000 KDPAR3: 000600 SDPAR3: 000000 UDPAR3: 000000 R10: 000000 FAC5H: 00000000000 KDPDR3: 077406 SDPDR3: 000000 UDPDR3: 000000 R11: 000000 FAC5L: 00000000000 KDPAR4: 001000 SDPAR4: 000000 UDPAR4: 000000 R12: 000000 FPS: 000004 KDPDR4: 077406 SDPDR4: 000000 UDPDR4: 000000 R13: 000000 FEA: 000000 KDPAR5: 001200 SDPAR5: 000000 UDPAR5: 000000 R14: 000000 FEC: 00 KDPDR5: 077406 SDPDR5: 000000 UDPDR5: 000000 R15: 000000 MMR0: 000201 KDPAR6: 001171 SDPAR6: 000000 UDPAR6: 000000 KSP: 141710 MMR1: 000000 KDPDR6: 077506 SDPDR6: 000000 UDPDR6: 000000 SSP: 000000 MMR2: 021626 KDPAR7: 007600 SDPAR7: 000000 UDPAR7: 001541 USP: 177756 MMR3: 000005 KDPDR7: 077506 SDPDR7: 000000 UDPDR7: 066016 PSW: 030000 KIPAR0: 000000 SIPAR0: 003612 UIPAR0: 001714 IREQ[0]: 00000000000 CM: 0 KIPDR0: 077406 SIPDR0: 000106 UIPDR0: 000006 TRAPS: 00000 PM: 3 KIPAR1: 000416 SIPAR1: 003700 UIPAR1: 000000 WAIT: 1 RS: 0 KIPDR1: 077506 SIPDR1: 000106 UIPDR1: 000000 STOP_TRAPS: 00001 FPD: 0 KIPAR2: 000616 SIPAR2: 000000 UIPAR2: 000000 STOP_VECA: 1 IPL: 0 KIPDR2: 077506 SIPDR2: 000000 UIPDR2: 000000 STOP_SPA: 1 T: 0 KIPAR3: 001016 SIPAR3: 000000 UIPAR3: 000000 PCQ[0]: 034412 N: 0 KIPDR3: 077506 SIPDR3: 000000 UIPDR3: 000000 WRU: 005 Z: 0 KIPAR4: 001216 SIPAR4: 000000 UIPAR4: 000000 2012/3/24 130
レジスタの指定方法 Stateの表示順に’-’で範囲を指定 sim> e pc-sp PC: 021630 R0: 140004 R1: 034272 R2: 005336 R3: 000200 R4: 000000 R5: 141724 SP: 141710 2012/3/24 131
MMUレジスタの指定方法 Stateの表示順に’-’で範囲を指定 sim> e KDPAR0-KDPDR7 KDPAR0: 000000 KDPDR0: 077506 KDPAR1: 000200 KDPDR1: 077506 Pdp11/40では、 KDPAR2: 000400 KDPDR2: 077506 KPAR,KPDRと呼んでい KDPAR3: KDPDR3: 000600 077406 たが、simhでは上位機種 KDPAR4: 001000 のレジスタ名で表示され KDPDR4: 077406 KDPAR5: 001200 るので、KDPAR,KDPDR KDPDR5: KDPAR6: 077406 001171 と読み替える KDPDR6: 077506 KDPAR7: 007600 KDPDR7: 077506 2012/3/24 132
メモリの調べ方 sim> e 0 // アドレス0を表示 0: 000417 sim> e 0/4 // アドレス0から4バイトを表示 0: 000417 2: 000004 sim> e 0-2 // アドレス0から2までを表示 0: 000417 2: 000004 2012/3/24 133
表示フォーマットの指定 -a ASCIIで表示 -c 文字列で表示 -m 命令列で表示 -o 8進で表示 -x 16進で表示 sim> e -m 54104/20 54104: 000013 54106: MOV R0,-(SP) 54110: BIC #177400,(SP) 54114: JSR PC,@#22100 54120: ADD #6,SP 2012/3/24 134
シンボルからアドレスを調べる sim> c //unixに戻る # nm /unix | grep main // mainのアドレス 022272T _main # nm /unix | grep proc 034476T _newproc 005206B _proc 043170T _procxmt 2012/3/24 135
mainでbreakしてMMUを調べる sim> break 022272 sim> boot rk0 @unix Breakpoint, PC: 022272 (JSR R5,22240) sim> e KDPAR0-KDPDR7 KDPAR0: 000000 KDPDR0: 077506 KDPAR1: 000200 KDPDR1: 077506 KDPAR2: 000400 KDPDR2: 077506 KDPAR3: 000600 KDPDR3: 077406 KDPAR4: 001000 KDPDR4: 077406 KDPAR5: 001200 KDPDR5: 077406 KDPAR6: 001171 KDPDR6: 077506 KDPAR7: 007600 KDPDR7: 077506 sim> 2012/3/24 136
自分のprocを見る#1 # nm /unix | grep _write 054124T _write Shellが#をwriteする箇所(カーネ 031700T _writei ルのエントリ)でbreakする 050176T _writep # Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> break 54124 sim> c [Enter] Breakpoint, PC: 054124 (JSR R5,22240) sim> 2012/3/24 137
自分のprocを見る#2  procのアドレスはu.u_procpでわかる  uは常にカーネル仮想の140000番地  一方でsimhのdebug機能は仮想アドレスを扱 えない  自分で変換する sim> e KDPAR6 KDPAR6: 001477 // 64バイトを1単位とした値  64バイト=6ビットシフト=8進数で00  Userは00147700にあるはず 2012/3/24 138
自分のprocを見る#3 sim> e 147700/100 147700: 141746 u_rsav[0] 147702: 141756 u_rsav[1] 147704: 000200 u_fsav[0] 0413: struct user 0414: { 147706: 000000 1 0415: int u_rsav[2]; /* save r5,r6 when exchanging stacks */ 147710: 000000 2 0416: int u_fsav[25]; /* save fp registers */ 0417: /* rsav and fsav must be first in structure */ 147712: 000000 3 0418: char u_segflg; /* flag for IO; user or kernel space */ 0419: char u_error; /* return error code */ 147714: 000000 4 0420: char u_uid; /* effective user id */ 0421: char u_gid; /* effective group id */ 147716: 000000 5 0422: char u_ruid; /* real user id */ 0423: char u_rgid; /* real group id */ : 0424: int u_procp; /* pointer to proc structure */ : 147764: 000000 24 147766: 000000 u_segflag/u_error 147770: 001400 u_uid/u_gid 147772: 001400 u_ruid/u_rgid 147774: 005262 u_procp 147776: 177737 2012/3/24 139
自分のprocを見る#4 sim> e -h 5262/40 5262: 0103 p_flag/p_stat 0358: struct proc 5264: 0064 p_sig/p_pri 0359: { 5266: 7F00 p_time/p_uid 0360: char p_stat; 5270: 0000 p_cpu 0361: char p_flag; 0362: char p_pri; /* priority, negative is high */ 5272: 42F2 p_ttyp 0363: char p_sig; /* signal number sent to this process * 5274: 000E p_pid 0364: char p_uid; /* user id, used to direct tty signals */ 0365: char p_time; /* resident time for scheduling */ 5276: 0001 p_ppid 0366: char p_cpu; /* cpu usage for scheduling */ 5300: 033F 0367: char p_nice; /* nice for scheduling */ 5302: 0048 0368: int p_ttyp; /* controlling tty */ 0369: int p_pid; /* unique process id */ 5304: 0000 0370: int p_ppid; /* process id of parent */ 5306: 0EDC 0371: int p_addr; /* address of swappable image */ 0372: int p_size; /* size of swappable image (*64 bytes) 5310: 0000 0373: int p_wchan; /* event process is awaiting */ 5312: 00CE 0374: int *p_textp; /* pointer to text structure */ 0375: 5314: 0000 pidは0xe=14(10進) 0376: } proc[NPROC]; 5316: 0000 5320: 0000 2012/3/24 140
自分のprocを見る#5 Breakpoint, PC: 054104 (JSR R5,22240) sim> nobreak 54104 sim> c # ps 14 - 29 ps # pidは0xe=14(10進) 2012/3/24 141
だらだらつづくよ 2012/3/24 142

V6 unix in okinawa

  • 1.
    オペ―レーティングシステムの読み書き Rev.2.1 @magoroku15 2012/3/24 日本Androidの会 沖縄支部 workshop@Naha Vol.17 - v6 unix 勉強会 2012/3/24 1
  • 2.
    他人の作品を読まなかった偉 大な作家,他人の筆づかいを 研究しなかった偉大な画家, 同僚の肩越しに技を盗まな かった腕のよい外科医,副操 縦席で実地の経験を積まな かった767機長――果たして, そんな人たちが本当にいるの でしょうか? Dave Thomas著 Code Reading―オープンソースから学ぶソフトウェア開発技法 より 2012/3/24 2
  • 3.
    最底辺活動  Lions本読書会に出てみた  本だけで読み解くのは大変  若干の解説を加えれば理解できる人は増えるはず  拙者のネタとしてGeekBarなどで話すことにした  これを最底辺活動と言っている ○ 他にもFPGAでPDP11を動かしたり、PICで遊んだり  UNIX v6を読む理由  最新のBSD, Linuxと実装は異なるが根幹は同じ  UNIX v6を読む事が現代のOSを理解する近道(だと 思う) 2012/3/24 3
  • 4.
    Lions’ Commentary onUNIX本読書会  月に1回貸し会議室(喫茶室ルノアール)にあつまってLions本の読書会  濃すぎる常連 @awazo 主催している人 @7shi 低レイヤ勉強会を多数主催、SilverLightで動くPDP-11のエミュレータ作った人 @xylnao11 古くからPDP-11とUNIXで遊んでて、最近安価なFPGAにPDP-11を載せてしまった人 @superhoge Blogがすごい、JavaScriptで動くPDP-11のエミュレータを作った人 @toyoshim 一人で読み終えてしまった人 @pakuqi v7講座を生徒役で参加してBlogに話した事の10倍くらいの内容をまとめてくれた人 @oraccha PDP11に詳しくて、V1も読んでる人 2012/3/24 4
  • 5.
    お勧めのBlogなど  http://d.hatena.ne.jp/takahirox/  http://www.tom-yam.or.jp/2238/index.html http://d.hatena.ne.jp/n7shi/  http://xiangcai.at.webry.info/  http://d.hatena.ne.jp/oraccha/ 2012/3/24 5
  • 6.
    @magoroku15の最近のネタ  ADK互換モジュールを1200円で作る http://www.slideshare.net/magoroku15/poormans-adk-11350123  OpenCoresのw11を動かしてみる http://www.slideshare.net/magoroku15/pdp11-onfpga  その他  横浜Android PF部,nagoyageekbar等で話したネタ は以下に置いてあります ○ http://www.slideshare.net/magoroku15/ 2012/3/24 6
  • 7.
    教材  Unix version6  テキスト ○ Lions Commentary on UNIX / ASCII Books 3990円  原著online版 ○ http://v6.cuzuco.com/v6.pdf ○ http://www.lemis.com/grog/Documentation/Lions/boo k.pdf  PDP-11/40  PDP-11 Processor Handbook  http://pdos.csail.mit.edu/6.097/readings/pdp11-40.pdf 2012/3/24 7
  • 8.
    ウォームアップ  OSの領域はプログラマにとっての最底辺部  アプリケーションプログラマ ○ 提供されるAPIを利用して、一般向けのサービス等を提供する人  システムプログラマ ○ ハードウェアの機能を利用して、API等プログラマ向け機能を提供 する人  必要とされる前提知識が異なる  命令レベルで計算機を(ぼんやりと)理解する必要あり  アプリケーションプログラマ向けの準備運動  Lions本 ○ 第2章 Fundamentals (PDFだとP4) 命令セット ○ 第9章 Hardware Interrupt and Traps (PDFのP42) 割り込み  PDP-11 Processor Handbook 2012/3/24 8
  • 9.
  • 10.
    システムプログラマから見た 計算機の構成要素  CPU  レジスタ ○ 汎用レジスタ 一般の演算命令に使用 ○ スタックポインタ スタックの先頭位置 ○ プログラムカウンタ 次に処理する命令の位置  フラグレジスタ 演算結果の一部を記憶  制御/状態レジスタ  Memory  外部記憶 2012/3/24 10
  • 11.
    PDP-11/40  レジスタ  16bitのレジスタが8個  r0-r7  r6はspスタックポインタ  r7はpcプログラムカウンタ  Processor Status Word  フラグレジスタ+状態レジスタ  memory  仮想アドレス幅は16bit 最大 64kbyte  物理アドレス幅は18bit 最大 256kbyte 2012/3/24 11
  • 12.
    レジスタ r0-r7 r0, r1 演算結果の一時的な保持、復帰値など r2, r3, r4 関数内のローカル変数。呼ばれた関数で退避し、 関数の終了時に復元 R5 フレームポインタ r6 (sp) スタックポインタ r7 (pc) 実行する命令の位置(アドレス) 2012/3/24 12
  • 13.
    Processor Status Word(PSW) 14-15 現在のモード (00 = kernel 11 = user) 12-13 以前のモード 5-7 プロセッサの優先度 (0~7) 4 トラップビット 3N 演算結果が負 2Z 演算結果が0 1V 演算結果がオーバフロー 0C 演算結果がキャリー 2012/3/24 13
  • 14.
    CPUの処理  以下を繰り返す 1. PCの示す位置(アドレス)から命令を読み込む 2. 解析して 3. 実行 4. 実行の副作用として以下の事象が発生する  PSWを書き換え  PCを書き換え  レジスタの書き換え  メモリの内容を書き換えたりする 2012/3/24 14
  • 15.
    CPUの処理 誤解しやすい点  PCもレジスタの一種  PC(r7)を書き換えると処理がジャンプ  比較と分岐は別命令 2012/3/24 15
  • 16.
  • 17.
    C言語の空間モデル  Text text  命令列の格納領域  書き換え禁止 data  Data bss  非0で初期化されたデータ領域  Bss  0で初期化されたデータ領域  Stack  自動(auto)変数の格納領域  関数呼び出し時のレジスタ退避域 stack 注)スレッドは、まだ存在しないので対象外 2012/3/24 17
  • 18.
    変数の宣言と空間配置 int i; // Bss a.out形式 実行時 (ファイル) (メモリ) Int j = 1; // Data header main() text text { data data static int k = 1; // Data int l = 1; // Stack bss int m; // Stack : } stack 2012/3/24 18
  • 19.
    関数呼び出しとスタック C main() { int e, f, r; e = 1; f = 2; r = func(e, f); } int c, d; func(a, b) { c = a; d = b; return c + d; } 2012/3/24 19
  • 20.
    関数とスタック namelist # nm-n a.out 000170T cret 0x78 000000a 0x00 crt0.o 000206B _c 000000t start 000210B _d 000004a a 000212B savr5 000006a b 177764a r 000030T 0x18 _main 177766a f 000030a src.o 177770a e 000030t ~main 000102T 0x42 _func 000102t ~func 000140T 0x60 _exit 000140a exit.o 000152T 0x6a csv 000152a csv.o 2012/3/24 20
  • 21.
    関数呼び出しとスタック ASM .globl _main .comm _c,2 .text .globl _d _main: .comm _d,2 ~~main: .globl _func ~e=177770 .text ~f=177766 _func: ~r=177764 ~~func: jsr r5,csv ~a=4 sub $6,sp ~b=6 mov $1,-10(r5) jsr r5,csv mov $2,-12(r5) mov 4(r5),_c mov -12(r5),(sp) mov 6(r5),_d mov -10(r5),-(sp) mov _c,r0 jsr pc,*$_func add _d,r0 tst (sp)+ jbr L2 mov r0,-14(r5) L2: jmp cret L1: jmp cret .globl .globl _c .data 2012/3/24 21
  • 22.
    csv 関数の入り口 Main: csv: : mov r5,r0 jsr r5,csv mov sp,r5 : mov r4,-(sp) mov r3,-(sp) mov r2,-(sp) tst -(sp) jmp (r0) r0 r1 r2 r3 r4 r5 r6:sp r7:pc ---- 0484, 0000, 0000, 0000, 0000, 0000, 047e, 0018 jsr r5,006a ---- 0484, 0000, 0000, 0000, 0000, 001c, 047c, 006a mov r5,r0 ---- 001c, 0000, 0000, 0000, 0000, 001c, 047c, 006c mov sp,r5 ---- 001c, 0000, 0000, 0000, 0000, 047c, 047c, 006e mov r4,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 047a, 0070 mov r3,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0478, 0072 mov r2,-(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0476, 0074 tst -(r6) -z-- 001c, 0000, 0000, 0000, 0000, 047c, 0474, 0076 2012/3/24 jmp (r0) 22
  • 23.
    cret 関数の出口 main: cret: : mov r5,r1 jmp cret mov -(r1),r4 : mov -(r1),r3 mov -(r1),r2 mov r5,sp mov (sp)+,r5 rts pc r0 r1 r2 r3 r4 r5 r6:sp r7:pc ---- 0003, 0462, 0000, 0000, 0000, 047c, 046e, 003e jmp 0078 ---- 0003, 0462, 0000, 0000, 0000, 047c, 046e, 0078 mov r5,r1 ---- 0003, 047c, 0000, 0000, 0000, 047c, 046e, 007a mov -(r1),r4 -z-- 0003, 047a, 0000, 0000, 0000, 047c, 046e, 007c mov -(r1),r3 -z-- 0003, 0478, 0000, 0000, 0000, 047c, 046e, 007e mov -(r1),r2 -z-- 0003, 0476, 0000, 0000, 0000, 047c, 046e, 0080 mov r5,r6 ---- 0003, 0476, 0000, 0000, 0000, 047c, 047c, 0082 mov (r6)+,r5 -z-- 0003, 0476, 0000, 0000, 0000, 0000, 047e, 0084 rts 0084 2012/3/24 23
  • 24.
    JSR – Jumpto SubRoutine jsr src,dst 以下と等価 1. MOV src,-(R6) srcをスタックにpush 2. MOV PC,src 次の命令のPCをsrcに転送 3. JMP dst dstにジャンプ jsr r5,0x006aの処理は以下 1. r5の値をstackに入れて、spを-2 (push) 2. Jsrの次に実行する(戻り先)アドレスをr5に入 れて 3. PCにdstを入れてジャンプ 2012/3/24 24
  • 25.
    RTS – ReTurnfrom Subroutine rts src 以下と等価 1. MOV src,PC 2. MOV (R6)+,src jsr r7の処理は以下 1. r7の値をPCに入れて(この場合は意味なし) 2. スタックの内容+2 (pop) してr7へ pcの書き換えによりジャンプ 2012/3/24 25
  • 26.
    main 引数の設定とfuncの呼出し # r0 r1 r2 r3 r4 r5 r6 r7 15: 001c,0000,0000,0000,0000,047c,0474,001c sub $6,r6 main 16: 001c,0000,0000,0000,0000,047c,046e,0020 mov $1,-8(r5) main 17: 001c,0000,0000,0000,0000,047c,046e,0026 mov $2,-a(r5) main 18: 001c,0000,0000,0000,0000,047c,046e,002c mov -a(r5),(r6) main 19: 001c,0000,0000,0000,0000,047c,046e,0030 mov -8(r5),-(r6) main 20: 001c,0000,0000,0000,0000,047c,046c,0034 jsr r7,*$0x0040 main 21: 001c,0000,0000,0000,0000,047c,046a,0042 jsr r5,0x006a func #15 #16-17 #18 #19 #20 0x046a r6→ 0x0038 0x046c r6→ 1 1 0x046e r6→ r6→ r6→ 2 2 2 0x0470 0x0472 2 2 2 2 0x0474 r6→ 1 1 1 1 0x0476 0x0478 0x047a 0x047c r5→ r5→ r5→ r5→ r5→ 2012/3/24 26
  • 27.
    funcの入り口 (func-csv) # r0 r1 r2 r3 r4 r5 r6 r7 21: 001c,0000,0000,0000,0000,047c,046a,0042 jsr r5,0x006a func 22: 001c,0000,0000,0000,0000,0046,0468,006a mov r5,r0 func-csv 23: 0046,0000,0000,0000,0000,0046,0468,006c mov r6,r5 func-csv 24: 0046,0000,0000,0000,0000,0468,0468,006e mov r4,-(r6) func-csv 25: 0046,0000,0000,0000,0000,0468,0466,0070 mov r3,-(r6) func-csv 26: 0046,0000,0000,0000,0000,0468,0464,0072 mov r2,-(r6) func-csv 27: 0046,0000,0000,0000,0000,0468,0462,0074 tst -(r6) func-csv 28: 0046,0000,0000,0000,0000,0468,0460,0076 jmp (r0) func #21 #23 #24-26 #27 0x045a 0x045c 0x045e 0x0460 r6→ 0x0462 r6→ r2 r2 0x0464 r3 r3 0x0466 r4 r4 0x0468 r6→ 0x047c r5,r6→ 0x047c r5→ 0x047c r5→ 0x047c 0x046a r6→ 0x0038 0x0038 0x0038 0x0038 0x046c 2012/3/24 27
  • 28.
    funcの出口 (func-cret) #r0 r1 r2 r3 r4 r5 r6 r7 34:0003,0000,0000,0000,0000,0468,0460,005c jmp 0x00078 func 35:0003,0000,0000,0000,0000,0468,0460,0078 mov r5,r1 func-cret 36:0003,0468,0000,0000,0000,0468,0460,007a mov -(r1),r4 func-cret 37:0003,0466,0000,0000,0000,0468,0460,007c mov -(r1),r3 func-cret 38:0003,0464,0000,0000,0000,0468,0460,007e mov -(r1),r2 func-cret 39:0003,0462,0000,0000,0000,0468,0460,0080 mov r5,r6 func-cret 40:0003,0462,0000,0000,0000,0468,0468,0082 mov (r6)+,r5 func-cret 41:0003,0462,0000,0000,0000,047c,046a,0084 rts r7 func-cret 42:0003,0462,0000,0000,0000,047c,046c,0038 tst (r6)+ main #27-34 #35 #36-38 #39 #40-41 0x045a 0x045c 0x045e 0x0460 r6→ r6→ r6→ 0x0462 r2 r2 r1→ r2 0x0464 r3 r3 r3 0x0466 r4 r4 r4 0x0468 r5→ 0x047c r5,r1→ 0x047c r5→ 0x047c r5,r6→ 0x047c r6→ 0x047c 0x046a 0x0038 0x0038 0x0038 0x0038 r6→ 0x0038 0x046c r6→ 2012/3/24 28
  • 29.
    関数とスタック  今のスタックr6:SP  前のスタックr5 r5:r6のペアでチェーン #20 #21 #27 0x0460 r6→ 0x0462 r2 0x0464 r3 0x0466 r4 0x0468 r6→ 0x047c r5→ 0x047c 0x046a 0x0038 0x0038 0x0038 0x046c 1 0x046e 2 0x0470 0x0472 2 0x0474 1 0x0476 0x0478 0x047a 0x047c r5→ 2012/3/24 29
  • 30.
    レジスタ r0-r7 もう一度 r0,r1 演算結果の一時的な保持、復帰値など r2, r3, r4 関数内のローカル変数。呼ばれた関数で退避し、 関数の終了時に復元 R5 フレームポインタ r6 (sp) スタックポインタ r7 (pc) 実行する命令の位置(アドレス) 2012/3/24 30
  • 31.
    C言語とasmの実行環境  asmから生成した命令列はスタックを必要と しない  C言語から生成した命令列は、実行時にス タックを必要とする  引数の受け渡し利用領域  自動変数の領域  呼び出し元レジスタ(r2,r3,r4)の退避領域  呼び出し先からの復帰アドレスの退避領域 2012/3/24 31
  • 32.
  • 33.
    getpid(2)の呼び出し C言語 main() { int i; i = getpid(); } 2012/3/24 33
  • 34.
    getpid(2)の呼び出し ASM .globl _main .text _main: ~~main: ~i=177770 jsr r5,csv tst -(sp) jsr pc,_getpid mov r0,-10(r5) L1: jmp cret .globl .data 2012/3/24 34
  • 35.
    getpid.s /usr/source/s4/getpid.s getpid = 20. .globl _getpid _getpid: mov r5,-(sp) mov sp,r5 sys getpid mov (sp)+,r5 rts pc 2012/3/24 35
  • 36.
    getpid.sの実行トレース 3: 001c, 0000, 0000, 0000, 0000, 0446, 043c, 001e jsr r7, 0x00034 4: 001c, 0000, 0000, 0000, 0000, 0446, 043a, 0034 mov r5, -(r6) 5: 001c, 0000, 0000, 0000, 0000, 0446, 0438, 0036 mov r6,r5 6: 001c, 0000, 0000, 0000, 0000, 0438, 0438, 0038 sys getpid 7: 001c, 0000, 0000, 0000, 0000, 0438, 0438, 003a mov (r6)+, r5 8: 001c, 0000, 0000, 0000, 0000, 0446, 043a, 003c rts r7 なんか変じゃない 2012/3/24 36
  • 37.
    open(2)の呼び出し C言語 main() { int f; f = open("hoge", 2); } 2012/3/24 37
  • 38.
    open(2)の呼び出し ASM .globl _main .text _main: ~~main: ~f=177770 jsr r5,csv tst -(sp) mov $2,(sp) mov $L2,-(sp) jsr pc,*$_open tst (sp)+ mov r0,-10(r5) L1: jmp cret .globl .data L2:.byte 150,157,147,145,0 2012/3/24 38
  • 39.
    open.s /usr/source/s5/open.s globl _open, cerror _open: mov r5,-(sp) mov sp,r5 mov 4(r5),0f mov 6(r5),0f+2 sys 0; 9f bec 1f jmp cerror 1: mov (sp)+,r5 rts pc .data 9: sys open; 0: ..; .. 2012/3/24 39
  • 40.
    open.sの実行トレース # r0 r1 r2 r3 r4 r5 r6 r7 1: 001c, 0000, 0000, 0000, 0000, 0482, 0474, 0034 mov r5,-(r6) 2: 001c, 0000, 0000, 0000, 0000, 0482, 0472, 0036 mov r6,r5 3: 001c, 0000, 0000, 0000, 0000, 0472, 0472 0038 mov 4(r5), 0x008e 4: 001c, 0000, 0000, 0000, 0000, 0472, 0472, 003e mov 6(r5), 0x0090 5: 001c, 0000, 0000, 0000, 0000, 0472, 0472, 0044 sys indir 0x0008c 6: 0003, 0000, 0000, 0000, 0000, 0472, 0472, 0048 bcc 0x0004e 7: 0003, 0000, 0000, 0000, 0000, 0472, 0472, 004e mov (r6)+, r5 8: 0003, 0000, 0000, 0000, 0000, 0482, 0474, 0050 rts なんか変じゃない 2012/3/24 40
  • 41.
    システムコールとライブラリコール  ライブラリ  マニュアルセクション3  例 fopen(3)  ユーザプログラムの空間で動作  必要に応じてシステムコールを呼び出す  システムコール  マニュアルセクション2  例 open(2)  OS内部の処理を呼び出す  呼出し方法は通常のCall/Returnとは異なる 2012/3/24 41
  • 42.
  • 43.
    Q:CPUが想定外の状態になると  たとえば以下の場合  不正なアドレスを参照した  未定義の命令を実行しようとした  0で割り算した  次の命令はどこから取り出せばよいか? 2012/3/24 43
  • 44.
    A: わな “Trap”を用意しておく CPUが身動きが取れなかった場合に次に命令 を取り出す場所を“わな”として仕掛けてお く  システムコールの呼び出しは特殊な「わな」  「わな」はCPUの処理過程に実行時の例外と して発生する  「わな」に引っかかる事を「例外」と呼ぶ 2012/3/24 44
  • 45.
    例外と割り込み  Lions本9章  Trap/例外  命令の実行に失敗した場合の「わな」仕掛ける  システムコールの呼び出しは特殊な「わな」  「わな」はCPUの処理過程で発生する  Interrupt/割り込み  CPUの処理に関係なく、外部からの「割り込み」  主に、外部装置の処理完了時に「割り込む」  外部装置の例 ○ テレタイプ、紙テープ、ラインプリンタ、磁気ディ スク、時計 2012/3/24 45
  • 46.
  • 47.
    例外の流れ 1. 例外/割り込みの発生 2. コンテキストの保存 ① PCとPSWをCPU内部に一時的に保存 ② 発生の要因を特定し、要因:アドレスの表(ベクター)を 検索 ③ ベクターをPCに設定 ④ 一時的に保存していたPC,PSWをカーネルスタックに 退避(push) 3. ハンドラの実行 4. コンテキストの復元 ① カーネルスタックにPC,PSWが格納されている状態で rtt命令を実行 ② rtt命令がカーネルスタックからPC,PSWを復元(pop) 5. 割り込み発生の次の命令を実行 2012/3/24 47
  • 48.
    例外ベクタ Vector Trap type Priority Location 004 Bus timeout 7 010 Illegal instruction 7 014 bpt-trace 7 020 iot 7 024 Power failure 7 030 Emulator trap 7 034 Trap instruction/ system entry 7 114 11/70 parity 7 240 Programmed interrupt 7 244 Floating point error 7 250 Segmentation violation 7 2012/3/24 48
  • 49.
    500 0500 / lowcore 0501 0505 br7 = 340 0506 0507 . = 0^. 0508 br 1f 0509 4 0510 0511 / trap vectors 0512 trap; br7+0. / bus error 0513 trap; br7+1. / illegal instruction 0514 trap; br7+2. / bpt-trace trap 0515 trap; br7+3. / iot trap 0516 trap; br7+4. / power fail 0517 trap; br7+5. / emulator trap 0518 trap; br7+6. / system entry 2012/3/24 49
  • 50.
    752 0752 .globl trap, call 0753 /* -------------- */ 0754 .globl _trap 0755 trap: 0756 mov PS,-4(sp) 0757 tst nofault 0758 bne 1f 0759 mov SSR0,ssr 0760 mov SSR2,ssr+4 0761 mov $1,SSR0 0762 jsr r0,call1; _trap 0763 / no return 2012/3/24 50
  • 51.
    2693 2693 trap(dev, sp,r1, nps, r0, pc, ps) 2694 { 2695 register i, a; 2696 register struct sysent *callp; 2697 2698 savfp(); 2699 if ((ps&UMODE) == UMODE) 2700 dev =| USER; 2750 2702 switch(dev) { : 2715 default: 2716 printf("ka6 = %o¥n", *ka6); 2717 printf("aps = %o¥n", &ps); 2718 printf("trap type %o¥n", dev); 2719 panic("trap"); 2721 case 0+USER: /* bus error * 2722 i = SIGBUS; 2723 break; 2012/3/24 51
  • 52.
    2751 2751 case 6+USER: /* sys call */ 2752 u.u_error = 0; 2753 ps =& ~EBIT; 2754 callp = &sysent[fuiword(pc-2)&077];  PCは”わな”に落ちた命令の次のアドレス  この命令は2バイトなので2を引いて  ユーザ空間からword(2バイト)を読み  下位6ビットを取り出す  この値をindexにしてsysentを見ると 2012/3/24 52
  • 53.
    2906 2906 * to the appropriate routine for processing a system call. 2907 * Each row contains the number of arguments 2908 * and a pointer to the routine. 2909 */ 2910 int sysent[] 2911 { 2912 0, &nullsys, /* 0 = indir */ 2913 0, &rexit, /* 1 = exit */ 2914 0, &fork, /* 2 = fork */ 2915 2, &read, /* 3 = read */ 2916 2, &write, /* 4 = write */ 2917 2, &open, /* 5 = open */ 2012/3/24 53
  • 54.
    システムコール番号 indir = 0. exit= 1. fork = 2. read = 3. write = 4. open = 5. close = 6. wait = 7. creat = 8. : : 2012/3/24 54
  • 55.
    システムコール呼出し処理 1. C言語で open(“aa”,2) 2. Libcで sys 5 を実行  sysはemulator trap instruction 3. ユーザプログラム空間から5を取り出して 4. sysent[5]でカーネル内のopen処理を見つけ 5. カーネル内部のopen処理を呼ぶ 2012/3/24 55
  • 56.
  • 57.
    割り込みベクタ Vector device priority Location 060 Teletype input 4 064 Teletype output 4 070 Paper tape input 4 074 Paper tape output 4 100 Line clock 6 104 Programmable clock 6 200 Line printer 4 220 RK disk driver 5 2012/3/24 57
  • 58.
  • 59.
  • 60.
    Clocks – lineand programmable  Line Clock  電源の周期から生成  AC>降圧(トランス)>整流(ダイオード)>コンデンサ  電源周波数のパルスを取り出せる  50HZで20ms間隔  昔の電気式デジタル時計はパルスxHZで1秒を生成  Programmable clock  指定の間隔でパルスを発生  PDP-11ではどちらかが必要 2012/3/24 60
  • 61.
    525 low.s 0525: . = 60^. Vector device entry 0526: klin; br4 0527: klou; br4 060 Teletype input klin 0528: 0529: . = 70^. 064 Teletype output klou 0530: pcin; br4 0531: pcou; br4 070 Paper tape input pcin 0532: 074 Paper tape output pcou 0533: . = 100^. 0534: kwlp; br6 100 Line clock kwlp 0535: kwlp; br6 0539: 104 Programmable clock kwlp 0540: . = 200^. 0541: lpou; br4 200 Line printer lpou 0542: 0543: . = 220^. 220 RK disk driver rkio 0544: rkio; br5 2012/3/24 61
  • 62.
    Line Clock  外部にあるclockから毎秒50個のパルスがCPUに 入る  1パルス毎に割り込みベクタの処理を実行 Clock CPU 2012/3/24 62
  • 63.
    kwlp low.s 0568: 0569: .globl_clock 0570: kwlp: jsr r0,call; _clock Cで書かれたclock()を呼ぶ 2012/3/24 63
  • 64.
    clock 3725: clock(dev, sp,r1, nps, r0, pc, ps) 3761: 3797: if(++lbolt >= HZ) { 3726: { 3762: /* 3798: if((ps&0340) != 0) 3727: register struct callo *p1, *p2; 3763: * callout 3799: return; 3728: register struct proc *pp; 3764: */ 3800: lbolt =- HZ; 3729: 3765: 3801: if(++time[1] == 0) 3730: /* 3766: spl5(); 3802: ++time[0]; 3731: * restart clock 3767: if(callout[0].c_time <= 0) { 3803: spl1(); 3732: */ 3768: p1 = &callout[0]; 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3733: 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3805: wakeup(tout); 3734: *lks = 0115; 3770: (*p1->c_func)(p1->c_arg); 3806: if((time[1]&03) == 0) { 3735: 3771: p1++; 3807: runrun++; 3736: /* 3772: } 3808: wakeup(&lbolt); 3737: * display register 3773: p2 = &callout[0]; 3809: } 3738: */ 3774: while(p2->c_func = p1->c_func) { 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) 3739: 3775: p2->c_time = p1->c_time; 3811: if (pp->p_stat) { 3740: display(); 3776: p2->c_arg = p1->c_arg; 3812: if(pp->p_time != 127) 3741: 3777: p1++; 3813: pp->p_time++; 3742: /* 3778: p2++; 3814: if((pp->p_cpu & 0377) > SCHMAG) 3743: * callouts 3779: } 3815: pp->p_cpu =- SCHMAG; else 3744: * if none, just return 3780: } 3816: pp->p_cpu = 0; 3745: * else update first non-zero time 3781: 3817: if(pp->p_pri > PUSER) 3746: */ 3782: /* 3818: setpri(pp); 3747: 3783: * lightning bolt time-out 3819: } 3748: if(callout[0].c_func == 0) 3784: * and time of day 3820: if(runin!=0) { 3749: goto out; 3785: */ 3821: runin = 0; 3750: p2 = &callout[0]; 3786: 3822: wakeup(&runin); 3751: while(p2->c_time<=0 && p2->c_func!=0) 3787: out: 3823: } 3752: p2++; 3788: if((ps&UMODE) == UMODE) { 3824: if((ps&UMODE) == UMODE) { 3753: p2->c_time--; 3789: u.u_utime++; 3825: u.u_ar0 = &r0; 3754: 3790: if(u.u_prof[3]) 3826: if(issig()) 3755: /* 3791: incupc(pc, u.u_prof); 3827: psig(); 3756: * if ps is high, just return 3792: } else 3828: setpri(u.u_procp); 3757: */ 3793: u.u_stime++; 3829: } 3758: 3794: pp = u.u_procp; 3830: } 3759: if((ps&0340) != 0) 3795: if(++pp->p_cpu == 0) 3831: } 3760: goto out; 3796: pp->p_cpu--; 2012/3/24 64
  • 65.
    clock 3725: clock(dev, sp,r1, nps, r0, pc, ps) 3761: 3797: if(++lbolt >= HZ) { 3726: { 3762: /* 3798: if((ps&0340) != 0) 3727: register struct callo *p1, *p2; 3763: * callout 3799: return; 3728: register struct proc *pp; 3764: */ 3800: lbolt =- HZ; 3729: 3765: 3801: if(++time[1] == 0) 3730: /* 3766: spl5(); 3802: ++time[0]; 3731: * restart clock 3767: if(callout[0].c_time <= 0) { 3803: spl1(); 3732: */ 3768: p1 = &callout[0]; 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3733: 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3805: wakeup(tout); 3734: *lks = 0115; 3770: (*p1->c_func)(p1->c_arg); 3806: if((time[1]&03) == 0) { 3735: 3771: p1++; 3807: runrun++; Callout 3736: /* 3772: } 3808: wakeup(&lbolt); 3737: * display register 3773: p2 = &callout[0]; 3809: } 3738: */ 3774: while(p2->c_func = p1->c_func) { 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) 3739: 3775: p2->c_time = p1->c_time; 3811: if (pp->p_stat) { Sched 3740: display(); 3776: p2->c_arg = p1->c_arg; 3812: if(pp->p_time != 127) 3741: 3777: p1++; 3813: pp->p_time++; 3742: /* 3778: p2++; 3814: if((pp->p_cpu & 0377) > SCHMAG) 3743: * callouts 3779: } 3815: pp->p_cpu =- SCHMAG; else 3744: * if none, just return 3780: } 3816: pp->p_cpu = 0; 3745: * else update first non-zero time 3781: 3817: if(pp->p_pri > PUSER) の入り口 3746: */ 3782: /* 3818: setpri(pp); 3747: 3783: * lightning bolt time-out 3819: } 3748: if(callout[0].c_func == 0) 3784: * and time of day 3820: if(runin!=0) { 3749: goto out; 3785: */ 3821: runin = 0; 3750: p2 = &callout[0]; 3786: 3822: wakeup(&runin); Acct 3751: while(p2->c_time<=0 && p2->c_func!=0) 3787: out: 3823: } 3752: p2++; 3788: if((ps&UMODE) == UMODE) { 3824: if((ps&UMODE) == UMODE) { 3753: p2->c_time--; 3789: u.u_utime++; 3825: u.u_ar0 = &r0; 3754: 3790: if(u.u_prof[3]) 3826: if(issig()) 3755: /* 3791: incupc(pc, u.u_prof); 3827: psig(); 3756: * if ps is high, just return 3792: } else 3828: setpri(u.u_procp); 3757: */ 3793: u.u_stime++; 3829: } 3758: 3794: pp = u.u_procp; 3830: } 3759: if((ps&0340) != 0) 3795: if(++pp->p_cpu == 0) 3831: } 3760: goto out; 3796: pp->p_cpu--; 2012/3/24 65
  • 66.
    謎の変数  u.u_time  u.u_stime pp->p_time  lbolt  time[2]  tout[2] 2012/3/24 66
  • 67.
    callout 構造 struct callo { int c_time; /* ticks between events */ int c_arg; /* function argument */ int (*c_func)(); /* function pointer */ } callout[NCALL]; 2012/3/24 67
  • 68.
    clock callout (1/2) 先頭のcalloutが未 3748: if(callout[0].c_func == 0) 登録なら、後続の 3749: goto out; 3750: p2 = &callout[0]; calloutも未登録と 判断して,Callout 処理をスキップ 3751: while(p2->c_time<=0 && p2->c_func!=0) 3752: p2++; 最初にc_timeが0 3753: p2->c_time--; より大きいCallout 3758: を見つけて、 3759: if((ps&0340) != 0) c_timeを-1 3760: goto out; ここより前の 3761: calloutはこの後処 理される 2012/3/24 68
  • 69.
    clock callout (2/2) 先頭に登録時間が 過ぎているcallout 3766: spl5(); が存在したら、 3767: if(callout[0].c_time <= 0) { 3768: p1 = &callout[0]; 3769: while(p1->c_func != 0 && p1->c_time <= 0) { 3770: (*p1->c_func)(p1->c_arg); 3771: p1++; 処理を実行 3772: } 3773: p2 = &callout[0]; 3774: while(p2->c_func = p1->c_func) { 3775: p2->c_time = p1->c_time; Callout配列を詰め 3776: p2->c_arg = p1->c_arg; る 3777: p1++; 3778: p2++; 3779: } 3780: } 2012/3/24 69
  • 70.
    clock acct USERモードから 3787: out: 割り込まれたら、 3788: if((ps&UMODE) == UMODE) { uのuser時間 3789: u.u_utime++; u_utimeを+1 3790: if(u.u_prof[3]) 3791: incupc(pc, u.u_prof); KERNELモードか 3792: } else ら割り込まれたら、 3793: u.u_stime++; uのsystem時間 u_stimeを+1 3794: pp = u.u_procp; 3795: if(++pp->p_cpu == 0) Procのp_cpuを加 3796: pp->p_cpu--; 算。オーバフロー しないように調整 ppはproc構造のポインタ、uはuser構造。共 にプロセスの管理構造1プロセスに1個づつ 2012/3/24 70
  • 71.
    clock schedの入り口(1/2) clockが呼ばれると、ここまでは必 3797: if(++lbolt >= HZ) { ず実行。lboltを+1して、HZ(50)を 3798: if((ps&0340) != 0) 3799: return; 超過したら、すなわち1秒経過し 3800: lbolt =- HZ; たら、 3801: if(++time[1] == 0) 3802: ++time[0]; lboltをリセット 3803: spl1(); 3804: if(time[1]==tout[1] && time[0]==tout[0]) 3805: wakeup(tout); 寝ているプロセスを起こす 3806: if((time[1]&03) == 0) { 3807: runrun++; 3808: wakeup(&lbolt); 3809: } 4秒ごとにlboltで待ってるプロセ スを起こす 今回はさらりと流す 2012/3/24 71
  • 72.
    clock schedの入り口(2/2) 3810: for(pp = &proc[0]; pp < &proc[NPROC]; pp++) プロセスの管理構造の配 3811: if (pp->p_stat) { 列をスキャン 3812: if(pp->p_time != 127) 3813: pp->p_time++; 3814: if((pp->p_cpu & 0377) > SCHMAG) p_statに値が入っている=プロ 3815: pp->p_cpu =- SCHMAG; elseセスが存在したら、p_timeを 3816: pp->p_cpu = 0; +1 3817: if(pp->p_pri > PUSER) 3818: setpri(pp); 3819: } 3820: if(runin!=0) { 3821: runin = 0; 3822: wakeup(&runin); runinが真なら、runinで待って 3823: } るプロセスをwakeup 3824: if((ps&UMODE) == UMODE) { 3825: u.u_ar0 = &r0; 3826: if(issig()) 3827: psig(); 3828: setpri(u.u_procp); 3829: } 3830: 2012/3/24 } 72
  • 73.
    謎の変数 回答  u.u_time USER時間  u.u_stime SYSTEM時間  pp->p_time プロセス起動からの時間  lbolt 毎秒60回++  time[2] 1970年からの秒  tout[2] sleep(2)が起きる時間 2012/3/24 73
  • 74.
    Unix のPPDA (PerProcDataArea) User構造体 user.h The user structure. One allocated per process. Contains all per process data that doesn't need to be referenced while the process is swapped.  Proc構造体 proc.h One structure allocated per active process. It contains all data needed about the process while the process may be swapped out. Other per process data (user.h) is swapped with the process. 2012/3/24 74
  • 75.
  • 76.
    a.outと空間の関係 CPU PC a.out header text text data data exec(2) bss ・関数を呼 び出すと自 3076 /* read in first 8 bytes 動的に割り 3077 * of file for segment 当てる。 3078 * sizes: ・呼び出し 3079 * w0 = 407/410/411 3080 * w1 = text size からの復帰 3081 * w2 = data size に備えてレ 3082 * w3 = bss size stack ジスタの内 3083 */ 容を退避。 2012/3/24 76
  • 77.
    例外/割り込みが起こると…….. •PSWのモードがユーザモー CPU PC ドからカーネルモードに変わ り、空間が切り替わる。 ① ② •事象に応じて、ベクター テーブルの値がPCに書き込 text ③ text A まれる。 data data •例外/割り込み発生時のPSW とPC(図中A)をスタックに pushする bss bss アプリケーション カーネル アプリケーションと stack stack カーネル 2つの空間?? 2012/3/24 77
  • 78.
    仮想アドレスと物理アドレス  現在の一般的な仮想記憶  仮想アドレス>物理アドレス  仮想メモリ量>物理メモリ量  実メモリ不足時は固定長(ページ)単位で2次記憶に退避 し必要時に復元 →ページング  PDP-11の仮想記憶  仮想アドレス<物理アドレス  仮想メモリ量<物理メモリ量  実メモリ不足時はプロセス全体を2次記憶に退避し実行 を抑止、一定時間経過後に全体を復元し実行可能に →スワッピング 2012/3/24 78
  • 79.
  • 80.
    アドレス変換機構の制御  リセット後は変換機構OFFで動作  OSの初期化時にプログラムで 1. 変換表を初期化 2. 変換機構をON  新しいプロセスの生成とメモリ獲得時に  OSが変換表を編集  プロセスの切り替え時に  変換表を入れ替え 2012/3/24 80
  • 81.
    PDP-11/40のメモリ管理機構  8個のカーネルモードページ  8個のユーザモードページ 1ページは最大8Kbyteの可変長  モード切り替えはPSWのモードに連動 2012/3/24 81
  • 82.
    Active Page Registers(APR) 2012/3/24 82
  • 83.
    APR- PARとPDRのペア PAR ベースアドレスを指定 PDR メモリ属性を指定 2012/3/24 83
  • 84.
    仮想記憶の有効化  bit0 ENABLE MANAGEMENTを1で有効 2012/3/24 84
  • 85.
    重要なアドレス m40.s USIZE = 16 Size of User Block (*64 = 1024 B) PS = 177776 Program Status Word SSR0 = 177572 Status Register KISA0 = 172340 Kernel Segment Address Register #0 KISA6 = 172354 Kernel Segment Address Register #6 KISD0 = 172300 Kernel Segment Descriptor Register #0 UISA0 = 177640 User Segment Address Register #0 UISA1 = 177642 User Segment Address Register #1 UISD0 = 177600 User Segment Descriptor Register #0 UISD1 = 177602 User Segment Descriptor Register #1 IO = 7600 I/O Segment Register 2012/3/24 85
  • 86.
    start 仮想空間の設定 0612: start: 0613: bit $1,SSR0 0614: bne start / loop if restart 0615: reset 0616: 0617: / initialize systems segments 0618: 0619: mov $KISA0,r0 0620: mov $KISD0,r1 0621: mov $200,r4 0622: clr r2 0623: mov $6,r3 0624: 1: 0625: mov r2,(r0)+ 0626: mov $77406,(r1)+ / 4k rw 0627: add r4,r2 0628: sob r3,1b 2012/3/24 86
  • 87.
    ユーザ空間の初期化 start 0630: / initialize user segment 0631: 0632: mov $_end+63.,r2 0633: ash $-6,r2 0634: bic $!1777,r2 0635: mov r2,(r0)+ / ksr6 = sysu 0636: mov $usize-1¥<8|6,(r1)+ 0637: 0638: / initialize io segment 0639: / set up counts on supervisor segments 0640: 0641: mov $IO,(r0)+ 0642: mov $77406,(r1)+ / rw 4k 0643: 0644: / get a sp and start segmentation 0645: 0646: mov $_u+[usize*64.],sp 0647: inc SSR0 2012/3/24 87
  • 88.
    bssとuのクリア 0646: mov $_u+[usize*64.],sp 0647: inc SSR0 0648: 0649: / clear bss 0650: 0651: mov $_edata,r0 0652: 1: 0653: clr (r0)+ 0654: cmp r0,$_end 0655: blo 1b 0656: 0657: / clear user block 0658: 0659: mov $_u,r0 0660: 1: 0661: clr (r0)+ 0662: cmp r0,$_u+[usize*64.] 0663: blo 1b 2012/3/24 88
  • 89.
    mainの呼び出し 0665: / setup previous mode and call main 0666: / on return, enter user mode at 0R 0667: 0668: mov $30000,PS 0669: jsr pc,_main 0670: mov $170000,-(sp) 0671: clr -(sp) 0672: rtt 2012/3/24 89
  • 90.
    最初のプロセスを生成 1627: if(newproc()) { 1628: expand(USIZE+1); 1629: estabur(0, 1, 0, 0); 1630: copyout(icode, 0, sizeof icode); 1631: /* 1632: * Return goes to loc. 0 of user init 1633: * code just copied out. 1634: */ 1635: return; 1636: } 1637: sched(); 2012/3/24 90
  • 91.
    icode 1511: /* 1512: *Icode is the octal bootstrap 1513: * program executed in user mode 1514: * to bring up the system. 1515: */ 1516: int icode[] 1517: { 1518: 0104413, /* sys exec; init; initp */ 1519: 0000014, 1520: 0000010, char* init = “/etc/init”; 1521: 0000777, /* br . */ 1522: 0000014, /* initp: init; 0 */ main( ) { 1523: 0000000, execl( init, init, 0 ); 1524: 0062457, /* init: </etc/init¥0> */ while( 1 ) ; 1525: 0061564, 1526: 0064457, } 1527: 0064556, 1528: 0000164, 1529: }; 2012/3/24 91
  • 92.
    Bootと空間の初期化 Boot直後 仮想記憶有効 最初のnewproc 最初のexec Initの実行 text text text text text data data data data data bss bss bss bss u user user user user user user IOレジスタ IOレジスタ IOレジスタ IOレジスタ icode exec text data bss stack 2012/3/24 92
  • 93.
    Unixの空間モデル  アプリとカーネルのアドレス域は分離  後に完全な仮想記憶サポートで少し変わる  各アプリケーションの空間は独立  カーネルの空間は  Text,Data,Bssは一部を除いて共有  Stackはアプリ毎に個別  Stackはstruct userのページの後半 2012/3/24 93
  • 94.
  • 95.
    PPDA - PerProcessor Data Area  OSを読み解く場合に最初に注目する構造  プロセスなどの実行単位毎に割り当てる領域  UNIXの場合はuserとproc 2012/3/24 95
  • 96.
    struct user 0413 structuser 0414 { 0415 int u_rsav[2]; /* save r5,r6 when exchanging stacks */ 0416 int u_fsav[25]; /* save fp registers */ 0417 /* rsav and fsav must be first in structure */ 0418 char u_segflg; /* flag for IO; user or kernel space */ 0419 char u_error; /* return error code */ 0420 char u_uid; /* effective user id */ 0421 char u_gid; /* effective group id */ 0422 char u_ruid; /* real user id */ 0423 char u_rgid; /* real group id */ 0424 int u_procp; /* pointer to proc structure */ 0436 int u_uisa[16]; /* prototype of segmentation addresses */ 043 int u_uisd[16]; /* prototype of segmentation descriptors */ 2012/3/24 96
  • 97.
    struct proc 0358 structproc 0359 { 0360 char p_stat; 0361 char p_flag; 0362 char p_pri; /* priority, negative is high */ 0363 char p_sig; /* signal number sent to this process */ 0364 char p_uid; /* user id, used to direct tty signals */ 0365 char p_time; /* resident time for scheduling */ 0366 char p_cpu; /* cpu usage for scheduling */ 0367 char p_nice; /* nice for scheduling */ 0368 int p_ttyp; /* controlling tty */ 0369 int p_pid; /* unique process id */ 0370 int p_ppid; /* process id of parent */ 0371 int p_addr; /* address of swappable image */ 0372 int p_size; /* size of swappable image (*64 bytes) */ 0373 int p_wchan; /* event process is awaiting */ 0374 int *p_textp; /* pointer to text structure */ 0376 } proc[NPROC]; 2012/3/24 97
  • 98.
    空間の切り替え機構 CPU PC アプリケーション カーネル PSW 00: カーネルモード text 11: ユーザモード text data 11用 00用 data APR0 APR0 APR1 APR1 bss APR2 APR2 bss APR3 APR3 APR4 APR4 APR5 APR5 APR6 APR6 struct user stack APR7 APR7 stack I/Oレジスタ 2012/3/24 98
  • 99.
    アプリケーションの動作時 PSW PC •PSWのモードはユーザ •PCはアプリのtextセグメントを指す text data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 99
  • 100.
    割り込みの発生-空間の切り替え PSW PC •PSWのモードがカーネルに •アドレス空間の制御レジスタ text がカーネルモードに切り替わる data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 100
  • 101.
    割り込みの発生-割り込みベクタ PSW PC •割り込みベクタのエントリを text 選択してPCに data bss user user user user kstack kstack kstack kstack IOレジスタ 1 text text text text data data data data bss bss bss bss •1のアドレスとpswをスタック に退避 stack stack stack stack 2012/3/24 101
  • 102.
    割り込みの発生-割り込みベクタ PSW PC •割り込みベクタのエントリを text 選択してPCに data bss Vector device 004 Bus Timeout user user Illegal instruction 010 user user kstack kstackBpt-trace kstack 014 kstack 024 iot Vector device entry IOレジスタ 034 Power failur 060 Teletype input klin 114 Emulator tarp 064 Teletype output klou instruction 1 text text text 070 text Paper tape input pcin 240 11/10 parity data data data 074 data Paper tape output pcou 244 Floting point error bss bss bss 100 bss Line clock kwlp 250 Segmentation violation 104 Programmable clock kwlp stack stack stack200 stack Line printer lpou 220 RK disk driver rkio 2012/3/24 102
  • 103.
    割り込みの発生ーハンドラの処理 PSW PC 2 text data bss user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 103
  • 104.
    割り込みのからの復帰 PSW PC カーネル用 APR0 text APR1 data APR2 APR3 bss APR4 APR5 APR6 user user user user APR7 kstack kstack kstack kstack IOレジスタ アプリ用 •処理が終わったら、スタックのPCと 1 text text PSW復元して割り込み時点に復帰 text text APR0 APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 104
  • 105.
     以上の説明は、割り込み発生時から、復帰ま でにプロセスの切り替えが発生しない場合  条件によっては以下の処理でプロセスの切り 替えが発生 1. procの検索と選択 2. user構造の切り替え 3. User構造+kstackに退避した状態の復元 4. アプリへの復帰 2012/3/24 105
  • 106.
    プロセスの切り替えー選択 PSW PC Procの検索(スケジューラ) text data ↑次に動かすアプリ(プロセス) bss のprocを見つける user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 106
  • 107.
    プロセスの切り替えーuserの切り替え PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ text text text text data data data data bss bss bss bss stack stack stack stack 2012/3/24 107
  • 108.
    プロセスの切り替えーuserの切り替え PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 空間管理用のレジスタを更新 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 108
  • 109.
    プロセスの切り替えーモードの切り替え •PSWをユーザに切り替え PSW PC •PCを復元 •退避していた、PCとレジスタを復元 text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 109
  • 110.
    プロセスの切り替えー完了 PSW PC text data proc bss user user user user kstack kstack kstack kstack IOレジスタ アプリ用 APR0 text text text text APR1 data data data data APR2 APR3 bss bss bss bss APR4 APR5 APR6 stack stack stack stack APR7 2012/3/24 110
  • 111.
    swtch 1/3 2178: swtch() 2179:{ 2180: static struct proc *p; 2181: register i, n; 2182: register struct proc *rp; 2183: 2184: if(p == NULL) 2185: p = &proc[0]; 2186: /* 2187: * Remember stack of caller 2188: */ 2189: savu(u.u_rsav); /* save r5,r6 when exchanging stacks */ 2190: /* 2191: * Switch to scheduler's stack 2192: */ 2193: retu(proc[0].p_addr); /* address of swappable image */ 2012/3/24 111
  • 112.
    swtch 2/3 2195: loop: 2196: runrun = 0; 2197: rp = p; 2198: p = NULL; 2199: n = 128; 2203: i = NPROC; 2204: do { 2205: rp++; 2206: if(rp >= &proc[NPROC]) 2207: rp = &proc[0]; 2208: if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)!=0) { 2209: if(rp->p_pri < n) { 2210: p = rp; 2211: n = rp->p_pri; 2212: } 2213: } 2214: } while(--i); 2012/3/24 112
  • 113.
    swtch 3/3 2223: rp = p; 2224: curpri = n; 2225: /* Switch to stack of the new process and set up 2226: * his segmentation registers. 2227: */ 2228: retu(rp->p_addr); /* address of swappable image */ 2229: sureg(); 2247: return(1); 2012/3/24 113
  • 114.
    あやしい関数 2189: savu(u.u_rsav); 今のプロセスのr5,r6をuに退避 2193: retu(proc[0].p_addr); uをproc0に 2228: retu(rp->p_addr); uを切り替えるプロセスに 2229: sureg(); uに退避してあるAPRをハードのARPに設定 2012/3/24 114
  • 115.
    savu 0725: _savu: 0726: bis $340,PS 0727: mov (sp)+,r1 0728: mov (sp),r0 0729: mov sp,(r0)+ 0730: mov r5,(r0)+ bis $340,PSでbit5-7を1に bic $340,PSでbit5-7を0に 0731: bic $340,PS 0732: jmp (r1) 725: 727-728: 729-730: 732: r0 u.u_rsav r1 戻り先 r6→ 戻り先 戻り先 u. u.u_rsav r6→ u.u_rsav sp r5 r1 戻り先 2012/3/24 115
  • 116.
    retu 0740: _retu: 0745: 1: 0741: bis $340,PS 0746: mov (r0)+,sp 0742: mov (sp)+,r1 0747: mov (r0)+,r5 0743: mov (sp),KISA6 0748: bic $340,PS 0744: mov $_u,r0 0749: jmp (r1) 0413 struct user 0414 { 0415 int u_rsav[2]/* save r5,r6 when exchanging stacks */ 742: 743-744: 746-747: u. sp r6→ 戻り先 戻り先 r5 r6→ p_addr r6→ p_addr カーネル用 r6→ p_addr APR0 APR1 APR2 APR3 APR4 戻り先 r1 戻り先 APR5 p_addr r1 戻り先 r6 sp r0 u.u_rsav APR7 r0 u.u_rsav r5 r5 2012/3/24 116
  • 117.
    sureg 1739: sureg() 1747: up =- 8; 1740: { 1748: rp =- 8; 1741: register *up, *rp, a; 1749: } 1742: 1750: while(rp > &UISA->r[0]) 1743: a = u.u_procp->p_addr; 1751: *--rp = *--up + a; 1744: up = &u.u_uisa[16]; 1754: up = &u.u_uisd[16]; 1745: rp = &UISA->r[16]; 1755: rp = &UISD->r[16]; 1746: if(cputype == 40) { 1765: } アプリ用 APR0 u.u_uisa[0-7] APR1 APR2 APR3 u.u_uisd[0-7] APR4 アプリの空間を設定 APR5 APR6 APR7 2012/3/24 117
  • 118.
  • 119.
    勉強会の記録  日本Androidの会 沖縄支部 workshop@Naha Vol.17  http://atnd.org/events/25533  http://togetter.com/li/278270  横浜Androidプラットフォーム第19回勉強会  http://atnd.org/events/26444  http://togetter.com/li/281856 2012/3/24 119
  • 120.
  • 121.
    Simhのインストールと起動  Ubuntuの場合 $ sudo apt-get install simh  pdp-11のシュミレーション $ pdp11 PDP-11 simulator V3.8-1 sim> 2012/3/24 121
  • 122.
    Unix v6のダウンロード  simh用Diskイメージのアーカイブ  http://simh.trailing-edge.com/software.html  V6のイメージは以下  http://simh.trailing-edge.com/kits/uv6swre.zip  ダウンロード $ wget http://simh.trailing-edge.com/kits/uv6swre.zip 2012/3/24 122
  • 123.
    Diskイメージの展開 $ unzip uv6swre.zip Archive:uv6swre.zip inflating: README.txt inflating: unix3_v6_rk.dsk inflating: unix1_v6_rk.dsk inflating: unix2_v6_rk.dsk inflating: unix0_v6_rk.dsk inflating: AncientUnix.pdf $ 2012/3/24 123
  • 124.
    設定ファイルの作成  uv6swre.zipを展開したディレクトリで以下の ファイルunixv6.cfを作成 $cat unixv6.cfg set cpu 11/40 set cpu u18 att rk0 unix0_v6_rk.dsk att rk1 unix1_v6_rk.dsk att rk2 unix2_v6_rk.dsk att rk3 unix3_v6_rk.dsk boot rk0 $ 2012/3/24 124
  • 125.
    Simhの起動 $ pdp11 unixv6.cfg PDP-11simulator V3.8-1 Disabling XQ @unix Login: root # 2012/3/24 125
  • 126.
    Simhのdebug機能  simhモードへの移行と復帰 # Ctrl+E Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> c #  simhモードへはCtrl+Eで  シミュレーションモードへはcで 2012/3/24 126
  • 127.
    ディバック機能を使う  カーネルのシンボルアドレスを調べる #chdir / # nm unix | grep savu 021636T _savu #  savu()のアドレスは021636, breakpointを指定する # Ctrl+E Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> break 021636 sim> c Breakpoint, PC: 021636 (SPL 6) savu()で止まった sim> 2012/3/24 127
  • 128.
    step実行 Breakpoint, PC: 021636(SPL 6) sim> e r5,sp,pc R5: 141742 SP: 141730 PC: 021636 sim> s 0725: _savu: Step expired, PC: 021640 (MOV (SP)+,R1) sim> s 0726: bis $340,PS Step expired, PC: 021642 (MOV (SP),R0) sim> s 0727: mov (sp)+,r1 Step expired, PC: 021644 (MOV SP,(R0)+) sim> s 0728: mov (sp),r0 Step expired, PC: 021646 (MOV R5,(R0)+) 0729: mov sp,(r0)+ sim> s 0730: mov r5,(r0)+ Step expired, PC: 021650 (SPL 0) sim> s 0731: bic $340,PS Step expired, PC: 021652 (JMP (R1)) 0732: jmp (r1) 2012/3/24 128
  • 129.
    レジスタの調べ方 /usr/share/doc/simh/simh_doc.pdfで説明 e {xamine} <list>examine memory or registers sim> e state // レジスタ等すべてを表示 PC: 021630 V: 0 KIPDR4: 077406 SIPDR4: 000000 UIPDR4: 000000 R0: 140004 C: 0 KIPAR5: 001416 SIPAR5: 000000 UIPAR5: 000000 R1: 034272 PIRQ: 000000 KIPDR5: 077406 SIPDR5: 000000 UIPDR5: 000000 R2: 005336 STKLIM: 000000 KIPAR6: 001616 SIPAR6: 000000 UIPAR6: 000000 R3: 000200 FAC0H: 00000000000 KIPDR6: 077406 SIPDR6: 000000 UIPDR6: 000000 R4: 000000 FAC0L: 00000000000 KIPAR7: 007600 SIPAR7: 000000 UIPAR7: 001541 R5: 141724 FAC1H: 00000000000 KIPDR7: 077506 SIPDR7: 000000 UIPDR7: 066016 SP: 141710 FAC1L: 00000000000 KDPAR0: 000000 SDPAR0: 000000 UDPAR0: 001714 R00: 140004 FAC2H: 00000000000 KDPDR0: 077506 SDPDR0: 000000 UDPDR0: 000006 R01: 034272 FAC2L: 00000000000 KDPAR1: 000200 SDPAR1: 000000 UDPAR1: 000000 R02: 005336 FAC3H: 00000000000 KDPDR1: 077506 SDPDR1: 000000 UDPDR1: 000000 R03: 000200 FAC3L: 00000000000 KDPAR2: 000400 SDPAR2: 000000 UDPAR2: 000000 R04: 000000 FAC4H: 00000000000 KDPDR2: 077506 SDPDR2: 000000 UDPDR2: 000000 R05: 141724 FAC4L: 00000000000 KDPAR3: 000600 SDPAR3: 000000 UDPAR3: 000000 R10: 000000 FAC5H: 00000000000 KDPDR3: 077406 SDPDR3: 000000 UDPDR3: 000000 R11: 000000 FAC5L: 00000000000 KDPAR4: 001000 SDPAR4: 000000 UDPAR4: 000000 R12: 000000 FPS: 000004 KDPDR4: 077406 SDPDR4: 000000 UDPDR4: 000000 R13: 000000 FEA: 000000 KDPAR5: 001200 SDPAR5: 000000 UDPAR5: 000000 R14: 000000 FEC: 00 KDPDR5: 077406 SDPDR5: 000000 UDPDR5: 000000 R15: 000000 MMR0: 000201 KDPAR6: 001171 SDPAR6: 000000 UDPAR6: 000000 KSP: 141710 MMR1: 000000 KDPDR6: 077506 SDPDR6: 000000 UDPDR6: 000000 SSP: 000000 MMR2: 021626 KDPAR7: 007600 SDPAR7: 000000 UDPAR7: 001541 USP: 177756 MMR3: 000005 KDPDR7: 077506 SDPDR7: 000000 UDPDR7: 066016 PSW: 030000 KIPAR0: 000000 SIPAR0: 003612 UIPAR0: 001714 IREQ[0]: 00000000000 CM: 0 KIPDR0: 077406 SIPDR0: 000106 UIPDR0: 000006 TRAPS: 00000 PM: 3 KIPAR1: 000416 SIPAR1: 003700 UIPAR1: 000000 WAIT: 1 RS: 0 KIPDR1: 077506 SIPDR1: 000106 UIPDR1: 000000 STOP_TRAPS: 00001 FPD: 0 KIPAR2: 000616 SIPAR2: 000000 UIPAR2: 000000 STOP_VECA: 1 IPL: 0 KIPDR2: 077506 SIPDR2: 000000 UIPDR2: 000000 STOP_SPA: 1 T: 0 KIPAR3: 001016 SIPAR3: 000000 UIPAR3: 000000 PCQ[0]: 034412 N: 0 KIPDR3: 077506 SIPDR3: 000000 UIPDR3: 000000 WRU: 005 Z: 0 KIPAR4: 001216 SIPAR4: 000000 UIPAR4: 000000 2012/3/24 129
  • 130.
    必要な内容に絞り込む PC,R0~R5,SP(KSP,USP) PSW KDPAR0~7 UDPAR0~7 PC: 021630 V: 0 KIPDR4: 077406 SIPDR4: 000000 UIPDR4: 000000 R0: 140004 C: 0 KIPAR5: 001416 SIPAR5: 000000 UIPAR5: 000000 R1: 034272 PIRQ: 000000 KIPDR5: 077406 SIPDR5: 000000 UIPDR5: 000000 R2: 005336 STKLIM: 000000 KIPAR6: 001616 SIPAR6: 000000 UIPAR6: 000000 R3: 000200 FAC0H: 00000000000 KIPDR6: 077406 SIPDR6: 000000 UIPDR6: 000000 R4: 000000 FAC0L: 00000000000 KIPAR7: 007600 SIPAR7: 000000 UIPAR7: 001541 R5: 141724 FAC1H: 00000000000 KIPDR7: 077506 SIPDR7: 000000 UIPDR7: 066016 SP: 141710 FAC1L: 00000000000 KDPAR0: 000000 SDPAR0: 000000 UDPAR0: 001714 R00: 140004 FAC2H: 00000000000 KDPDR0: 077506 SDPDR0: 000000 UDPDR0: 000006 R01: 034272 FAC2L: 00000000000 KDPAR1: 000200 SDPAR1: 000000 UDPAR1: 000000 R02: 005336 FAC3H: 00000000000 KDPDR1: 077506 SDPDR1: 000000 UDPDR1: 000000 R03: 000200 FAC3L: 00000000000 KDPAR2: 000400 SDPAR2: 000000 UDPAR2: 000000 R04: 000000 FAC4H: 00000000000 KDPDR2: 077506 SDPDR2: 000000 UDPDR2: 000000 R05: 141724 FAC4L: 00000000000 KDPAR3: 000600 SDPAR3: 000000 UDPAR3: 000000 R10: 000000 FAC5H: 00000000000 KDPDR3: 077406 SDPDR3: 000000 UDPDR3: 000000 R11: 000000 FAC5L: 00000000000 KDPAR4: 001000 SDPAR4: 000000 UDPAR4: 000000 R12: 000000 FPS: 000004 KDPDR4: 077406 SDPDR4: 000000 UDPDR4: 000000 R13: 000000 FEA: 000000 KDPAR5: 001200 SDPAR5: 000000 UDPAR5: 000000 R14: 000000 FEC: 00 KDPDR5: 077406 SDPDR5: 000000 UDPDR5: 000000 R15: 000000 MMR0: 000201 KDPAR6: 001171 SDPAR6: 000000 UDPAR6: 000000 KSP: 141710 MMR1: 000000 KDPDR6: 077506 SDPDR6: 000000 UDPDR6: 000000 SSP: 000000 MMR2: 021626 KDPAR7: 007600 SDPAR7: 000000 UDPAR7: 001541 USP: 177756 MMR3: 000005 KDPDR7: 077506 SDPDR7: 000000 UDPDR7: 066016 PSW: 030000 KIPAR0: 000000 SIPAR0: 003612 UIPAR0: 001714 IREQ[0]: 00000000000 CM: 0 KIPDR0: 077406 SIPDR0: 000106 UIPDR0: 000006 TRAPS: 00000 PM: 3 KIPAR1: 000416 SIPAR1: 003700 UIPAR1: 000000 WAIT: 1 RS: 0 KIPDR1: 077506 SIPDR1: 000106 UIPDR1: 000000 STOP_TRAPS: 00001 FPD: 0 KIPAR2: 000616 SIPAR2: 000000 UIPAR2: 000000 STOP_VECA: 1 IPL: 0 KIPDR2: 077506 SIPDR2: 000000 UIPDR2: 000000 STOP_SPA: 1 T: 0 KIPAR3: 001016 SIPAR3: 000000 UIPAR3: 000000 PCQ[0]: 034412 N: 0 KIPDR3: 077506 SIPDR3: 000000 UIPDR3: 000000 WRU: 005 Z: 0 KIPAR4: 001216 SIPAR4: 000000 UIPAR4: 000000 2012/3/24 130
  • 131.
    レジスタの指定方法 Stateの表示順に’-’で範囲を指定 sim> e pc-sp PC:021630 R0: 140004 R1: 034272 R2: 005336 R3: 000200 R4: 000000 R5: 141724 SP: 141710 2012/3/24 131
  • 132.
    MMUレジスタの指定方法 Stateの表示順に’-’で範囲を指定 sim> e KDPAR0-KDPDR7 KDPAR0: 000000 KDPDR0: 077506 KDPAR1: 000200 KDPDR1: 077506 Pdp11/40では、 KDPAR2: 000400 KDPDR2: 077506 KPAR,KPDRと呼んでい KDPAR3: KDPDR3: 000600 077406 たが、simhでは上位機種 KDPAR4: 001000 のレジスタ名で表示され KDPDR4: 077406 KDPAR5: 001200 るので、KDPAR,KDPDR KDPDR5: KDPAR6: 077406 001171 と読み替える KDPDR6: 077506 KDPAR7: 007600 KDPDR7: 077506 2012/3/24 132
  • 133.
    メモリの調べ方 sim> e 0 // アドレス0を表示 0: 000417 sim> e 0/4 // アドレス0から4バイトを表示 0: 000417 2: 000004 sim> e 0-2 // アドレス0から2までを表示 0: 000417 2: 000004 2012/3/24 133
  • 134.
    表示フォーマットの指定 -a ASCIIで表示 -c 文字列で表示 -m 命令列で表示 -o 8進で表示 -x 16進で表示 sim> e -m 54104/20 54104: 000013 54106: MOV R0,-(SP) 54110: BIC #177400,(SP) 54114: JSR PC,@#22100 54120: ADD #6,SP 2012/3/24 134
  • 135.
    シンボルからアドレスを調べる sim> c //unixに戻る # nm /unix | grep main // mainのアドレス 022272T _main # nm /unix | grep proc 034476T _newproc 005206B _proc 043170T _procxmt 2012/3/24 135
  • 136.
    mainでbreakしてMMUを調べる sim> break 022272 sim>boot rk0 @unix Breakpoint, PC: 022272 (JSR R5,22240) sim> e KDPAR0-KDPDR7 KDPAR0: 000000 KDPDR0: 077506 KDPAR1: 000200 KDPDR1: 077506 KDPAR2: 000400 KDPDR2: 077506 KDPAR3: 000600 KDPDR3: 077406 KDPAR4: 001000 KDPDR4: 077406 KDPAR5: 001200 KDPDR5: 077406 KDPAR6: 001171 KDPDR6: 077506 KDPAR7: 007600 KDPDR7: 077506 sim> 2012/3/24 136
  • 137.
    自分のprocを見る#1 # nm /unix| grep _write 054124T _write Shellが#をwriteする箇所(カーネ 031700T _writei ルのエントリ)でbreakする 050176T _writep # Simulation stopped, PC: 021630 (MOV (SP)+,177776) sim> break 54124 sim> c [Enter] Breakpoint, PC: 054124 (JSR R5,22240) sim> 2012/3/24 137
  • 138.
    自分のprocを見る#2  procのアドレスはu.u_procpでわかる  uは常にカーネル仮想の140000番地 一方でsimhのdebug機能は仮想アドレスを扱 えない  自分で変換する sim> e KDPAR6 KDPAR6: 001477 // 64バイトを1単位とした値  64バイト=6ビットシフト=8進数で00  Userは00147700にあるはず 2012/3/24 138
  • 139.
    自分のprocを見る#3 sim> e147700/100 147700: 141746 u_rsav[0] 147702: 141756 u_rsav[1] 147704: 000200 u_fsav[0] 0413: struct user 0414: { 147706: 000000 1 0415: int u_rsav[2]; /* save r5,r6 when exchanging stacks */ 147710: 000000 2 0416: int u_fsav[25]; /* save fp registers */ 0417: /* rsav and fsav must be first in structure */ 147712: 000000 3 0418: char u_segflg; /* flag for IO; user or kernel space */ 0419: char u_error; /* return error code */ 147714: 000000 4 0420: char u_uid; /* effective user id */ 0421: char u_gid; /* effective group id */ 147716: 000000 5 0422: char u_ruid; /* real user id */ 0423: char u_rgid; /* real group id */ : 0424: int u_procp; /* pointer to proc structure */ : 147764: 000000 24 147766: 000000 u_segflag/u_error 147770: 001400 u_uid/u_gid 147772: 001400 u_ruid/u_rgid 147774: 005262 u_procp 147776: 177737 2012/3/24 139
  • 140.
    自分のprocを見る#4 sim> e -h5262/40 5262: 0103 p_flag/p_stat 0358: struct proc 5264: 0064 p_sig/p_pri 0359: { 5266: 7F00 p_time/p_uid 0360: char p_stat; 5270: 0000 p_cpu 0361: char p_flag; 0362: char p_pri; /* priority, negative is high */ 5272: 42F2 p_ttyp 0363: char p_sig; /* signal number sent to this process * 5274: 000E p_pid 0364: char p_uid; /* user id, used to direct tty signals */ 0365: char p_time; /* resident time for scheduling */ 5276: 0001 p_ppid 0366: char p_cpu; /* cpu usage for scheduling */ 5300: 033F 0367: char p_nice; /* nice for scheduling */ 5302: 0048 0368: int p_ttyp; /* controlling tty */ 0369: int p_pid; /* unique process id */ 5304: 0000 0370: int p_ppid; /* process id of parent */ 5306: 0EDC 0371: int p_addr; /* address of swappable image */ 0372: int p_size; /* size of swappable image (*64 bytes) 5310: 0000 0373: int p_wchan; /* event process is awaiting */ 5312: 00CE 0374: int *p_textp; /* pointer to text structure */ 0375: 5314: 0000 pidは0xe=14(10進) 0376: } proc[NPROC]; 5316: 0000 5320: 0000 2012/3/24 140
  • 141.
    自分のprocを見る#5 Breakpoint, PC: 054104(JSR R5,22240) sim> nobreak 54104 sim> c # ps 14 - 29 ps # pidは0xe=14(10進) 2012/3/24 141
  • 142.