仮想記憶入門 bsd-4.3を例題に
TRANSCRIPT
UNIX V6 セミナー (4)
@magoroku15
2013/5/11
今日の内容
• 過去:UNIX v6, 未来:4.4BSDとの関連
• 仮想記憶の空間構成
• スワッピングとページング
• 4.3BSDを例にした仮想記憶管理の解析
• 参考資料 – Freebsd-53-kernel-mm.pptx 七誌さん
過去:UNIX V6, 未来:4.4BSDとの関連
4.3BSD
カーネルのメモリ構成
※アドレスは上を0としたため、本とは上下反転
カーネル
ユーザー
0
4GB
1. 常駐型 FreeBSDなど
カーネル ユーザー
2. 切替型 UNIX V6など
http://sdrv.ms/160BJhK より
常駐型
カーネル
プロセスA
0
4GB
永続マップ カーネル
プロセスB
• カーネルが同じアドレスに常駐する
タスクスイッチで入替
http://sdrv.ms/160BJhK より
切替型
0
4GB
カーネル プロセスA
• 広いメモリ空間を使うことができる
プロセスB
http://sdrv.ms/160BJhK より
データの受け渡し
カーネル
ユーザー
0
4GB
1. 常駐型 直接参照(高速)
2. 切替型 特殊命令でコピー(遅い)
カーネル ユーザー
※カーネルは自由にユーザーのメモリを読めるが、逆は不可 http://sdrv.ms/160BJhK より
メモリ管理から見た4.3BSDの特徴
• UNIX v{6|7}の空間を切り替え型から常駐型に変更 • On demand paging
– clock algorithm • One handからTwo handへの変更
• 共有メモリはtextのみ – COW, shm, 動的なbufなどは存在しない
• 限定的なカーネルメモリアロケータ – mbufのみ
• VAX-11依存の実装 – PTEにOSの管理情報を置く – 参照ビットのエミュレーション
4.3BSDを理解する意味
• UNIXの系譜では第2世代の最後 – 第一世代 UNIX v{6|7} 切り替え型
– 第二世代 UNIX 32v~4.3BSD 常駐型
– 第三世代 Mach, 4.4BSD他 +広義の単一レベル記憶 • 仮想空間にvnode,offsetで表現可能なファイルをマップ可能になる
• 次の事項の理解に有用 – MMUの仕組み
– ページングの原理と実装
– 実メモリ管理
• UNIX v{6|7}の構成の変化は少なく連続性がある – ファイル構成・関数構成
仮想記憶の空間構成
空間の構成
• 単一仮想空間
–一枚の空間を仮想化する
–一枚の空間に複数のプロセスが同居
– メモリの拡張が主な目的
• 多重仮想空間
–複数の空間を仮想化する
–一枚の空間に1個のプロセスを配置
–システムの保護
空間構成 多重仮想空間
単一仮想空間
多重仮想空間の管理構造
ユーザ
物理メモリ管理構造
仮想-物理アドレス変換表
カーネル
アドレス変換機構
• アドレスの変換単位はページ – 数Kbyte程度
– Aligned
• アドレスの変換表をOSが用意してCPU(MMU)に通知
• MMUが仮想アドレスを実アドレスに変換
変換表
CR
アドレス変換表
アドレスと変換表の関係
2レベル変換表
仮想空間の利点
• 複数の独立したアドレス空間
– アドレス空間の間でメモリの書き換え・参照ができない
– プログラムAのバグがプログラムBに影響を与えない
– プログラムAのメモリをプログラムBから参照できない
• 全てのプログラムが同じアドレスを利用可能
– リロケータブルである必要がない
– コンパイラ・リンカの負担が少ない
スワッピングとページング
スワッピング
• スワップアウト – 最も優先度の低いプログラムを選択し – プログラムをスケジュールの対象から外す – プログラムのメモリ全体をスワップデバイスに退避(write)
• スワップイン – 実行するプロセスがスワップアウトさせていたら – メモリを確保-不足したら他のプロセスをスワップアウト
– スワップデバイスに退避してあるメモリイメージをメモリに復元(read)
– プログラムをスケジュールの対象に含める
• スワッピングとページングは明確に区別せずに使われる場合があるので注意
スワッピングの特徴
• プログラム全体を退避・復元
–アドレス空間の拡張に伴ってI/O量が増加
–大きなコストを払ってスワップインしても、その後の挙動は予想できない
• メモリ・フラグメンテーションの解消手段
– UNIX v6等の実装
• ページングと比較してハードウェアの負担が少ない
ページング
• ページアウト 1. メモリが不足 2. 最近使われていない物理メモリ(ページ)を特定
3. 物理メモリが仮想メモリとして利用されていたら変換対象から外す
4. 物理メモリをページングデバイスに退避
• ページイン 1. 割り当て済の仮想メモリをプログラムが参照 2. アドレス変換例外で割り込みが発生しOSに制御が移る 3. OSが該当プロセス+アドレスから退避済の物理メモリ(ページ)を特定する
4. 物理メモリを読み込んで仮想メモリから変換可能とする 5. 割り込みから復帰
ページングの特徴
• 一般的な運用では – 仮想空間の総量 > 物理メモリ – 物理メモリの奪い合い – メモリ参照に局所性が有る場合
• ゆっくり奪い合う→変換例外とその対応のコストは無視できる
– メモリ参照に局所性が無い場合 • はげしく奪い合う→変換例外とその対応コストが無視できない
• 局所的な参照を前提として使われていないメモリの特定が必要 – 最近利用されていないメモリは将来も利用されない可能性が高い
– どうやって?
LRU - Least Recently Used
• 「最近使われていないメモリは、将来も使われない可能性が高い」事を前提とする
• メモリの参照頻度を記録して、最近使われていないメモリを予想する
–近年のCPUは参照の有無をアドレス変換表にHWが記録する
• X86, ARMなど (除くVAX-11)
• 多くは1bit(参照の有無)のみを記録
仮想記憶の構成
4.3BSDを例に
解析の対象
• 4.3BSD
– 1986年、カルフォルニア大学バークレー校で開発・配布された近代UNIX
–仮想記憶・TCP/IP・拡張ファイルシステムが特徴
– ソースの入手にはAT&T UNIXのライセンスが必要
–今回は以下を利用http://www.tamacom.com/tour-j.html
4.3BSD VAX-11の空間構成
• 32bit = 4Gbyteの空間を4分割(4セグメント)
unused
kernel
User stack
User Text User Data
0x4000.000
0x0000.000
0x8000.000
0xC000.000
0xFFFF.FFFF
P0
P1
System
Reserved
• P0,P1 にUserプログラム • P1にはKernel Stack・Uも配置 • System リージョンにカーネルを配置 • copy{in|out}は同一空間の転送
1142 /* 1143 * Copy specified amount of data from user space into the kernel 1144 * Copyin(from, to, len) 1145 * r1 == from (user source address) 1146 * r3 == to (kernel destination address) 1147 * r5 == length 1148 */ 1149 .align 1 1150 JSBENTRY(Copyin, R1|R3|R5) 1151 cmpl r5,$(NBPG*CLSIZE) # probing one page or less ? 1152 bgtru 1f # no 1153 prober $3,r5,(r1) # bytes accessible ? 1154 beql ersb # no 1155 movc3 r5,(r1),(r3) 1156 /* clrl r0 # redundant */ 1157 rsb
VAX-11の仮想記憶
• 512byte/1ページ 9bit
• 32bitのアドレス幅上位2bitがリージョン – P0=00,P1=01,S=10
32bit
Page offset 9bit
Segmant 2bit
Virtual Page Number 21bit
セグメントとページテーブル
• 各セグメントはPTEの配列=ページテーブルとして表現 –セグメントベースレジスタ:SEGBR
–セグメントリミットレジスタ:SEGLR
• 各リージョンは2^21個のページを持つ – 1リージョン8MBのページテーブルが必要
– P0,P1,Sで24Mbyte/プロセス必要
– P0,P1のページテーブルはSのカーネル仮想に配置
セグメント用のレジスタ
• root/machine/mtpr.h 18 #define P0BR 0x8 /* p0 base register */ 19 #define P0LR 0x9 /* p0 length register */ 20 #define P1BR 0xa /* p1 base register */ 21 #define P1LR 0xb /* p1 length register */ 22 #define SBR 0xc /* system segment base register */ 23 #define SLR 0xd /* system segment length register */
unused
kernel
User stack Kernal stack
User Text User Data
P0BR
P1BR
SBR
P0LR
P1LR
SLR
セグメントレジスタの設定
• root/GENERIC/sys/vmmac.h 88 #define setp0br(x) (u.u_pcb.pcb_p0br = (x), mtpr(P0BR, x)) 89 #define setp0lr(x) (u.u_pcb.pcb_p0lr = ¥ 90 (x) | (u.u_pcb.pcb_p0lr & AST_CLR), ¥ 91 mtpr(P0LR, x)) 92 #define setp1br(x) (u.u_pcb.pcb_p1br = (x), mtpr(P1BR, x)) 93 #define setp1lr(x) (u.u_pcb.pcb_p1lr = (x), mtpr(P1LR, x)) 94 #define initp1br(x) ((x) - P1PAGES) UNIX v6がuにアドレス変換レジスタの内容を持っていたのと同じ
実メモリ(ページ)管理
4.3BSDを例に
構造:cmap
• h/cmap.h 27 struct cmap 28 { 29 unsigned short c_next, /* index of next free list entry */ 30 c_prev, /* index of previous free list entry */ 31 c_hlink; /* hash link for <blkno,mdev> */ 32 unsigned short c_ndx; /* index of owner proc or text */ 33 unsigned int c_page:21, /* virtual page number in segment */ 34 c_lock:1, /* locked for raw i/o or pagein */ 35 c_want:1, /* wanted */ 36 c_intrans:1, /* intransit bit */ 37 c_free:1, /* on the free list */ 38 c_gone:1, /* associated page has been released */ 39 c_type:2, /* type CSYS or CTEXT or CSTACK or CDATA */ 40 :4, /* to longword boundary */ 41 c_blkno:24, /* disk block this is a copy of */ 42 c_mdev:8; /* which mounted dev this is from */ 43 };
Cmapの初期化
• root/vax/machdep.c 105 v = (caddr_t)(0x80000000 | (firstaddr * NBPG)); 106 #define valloc(name, type, num) ¥ 107 (name) = (type *)v; v = (caddr_t)((name)+(num)) 108 #define valloclim(name, type, num, lim) ¥ 109 (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) 110 valloclim(inode, struct inode, ninode, inodeNINODE); 111 valloclim(file, struct file, nfile, fileNFILE); 112 valloclim(proc, struct proc, nproc, procNPROC); 113 valloclim(text, struct text, ntext, textNTEXT); 114 valloc(cfree, struct cblock, nclist); 115 valloc(callout, struct callout, ncallout); 116 valloc(swapmap, struct map, nswapmap = nproc * 2); 117 valloc(argmap, struct map, ARGMAPSIZE); 118 valloc(kernelmap, struct map, nproc); 119 valloc(mbmap, struct map, nmbclusters/4); 120 valloc(namecache, struct namecache, nchsize); 177 ncmap = (maxmem*NBPG - ((int)(v + bufpages*CLBYTES) &~ 0x80000000)) / 178 (CLBYTES + sizeof(struct cmap)) + 2; 179 valloclim(cmap, struct cmap, ncmap, ecmap);
パラメタを参照して各構造を 割り当て(v6では配列だった)
現在のvからmaxmemまでの アドレスに応じたcmapを確保
ページの割り当て:memall()
• sys/vm_mem.c 159 /* 160 * Allocate memory - 161 * 162 * The free list appears as a doubly linked list 163 * in the core map with cmap[0] serving as a header. 164 */ 165 memall(pte, size, p, type) /* */ 166 register struct pte *pte; 167 int size; 168 struct proc *p; 169 { 186 for (i = size; i > 0; i -= CLSIZE) { 210 case CDATA: 211 rpte = dptopte(rp, c->c_page); 212 break; 213 222 switch (type) { 233 case CDATA:
234 c->c_page = vtodp(p, ptetov(p, pte)); 235 c->c_ndx = p->p_ndx; 236 break; 272 pf = cmtopg(curpos); 273 for (j = 0; j < CLSIZE; j++) 274 *(int *)pte++ = pf++; 275 c->c_free = 0; 276 c->c_gone = 0; 277 if (c->c_intrans || c->c_want) 278 panic("memall intrans|want"); 279 c->c_lock = 1; 280 c->c_type = type; 281 } 282 splx(s); 283 return (size); 284 }
ページング
構造:pte
• vax/pte.h 20 struct pte 21 { 22 unsigned int pg_pfnum:21, /* core page frame number or 0 */ 23 :2, 24 pg_vreadm:1, /* modified since vread (or with _m) */ 25 pg_swapm:1, /* have to write back to swap */ 26 pg_fod:1, /* is fill on demand (=0) */ 27 pg_m:1, /* hardware maintained modified bit */ 28 pg_prot:4, /* access control */ 29 pg_v:1; /* valid bit */ 30 };
pfnum V prot M 0 soft
59 #define PG_NOACC 0 60 #define PG_KW 0x10000000 61 #define PG_KR 0x18000000 62 #define PG_UW 0x20000000 63 #define PG_URKW 0x70000000 64 #define PG_URKR 0x78000000
処理:pageout()
• sys/vm_page.c +696 696 loop: 705 (void) splbio(); 706 if (bclnlist != NULL) { 707 (void) spl0(); 708 cleanup(); 709 goto loop; 710 } 711 sleep((caddr_t)&proc[2], PSWP+1); 712 (void) spl0(); 713 count = 0; 714 pushes = 0; 715 while (nscan < desscan && freemem < lotsfree) { 720 if (checkpage(fronthand, FRONT)) 721 count = 0; 722 if (checkpage(backhand, BACK)) 723 count = 0; 724 cnt.v_scan++; 725 nscan++; 726 if (++fronthand >= maxhand) { 727 fronthand = 0; 728 cnt.v_rev++; 729 if (count > 2) { 735 goto loop;
736 } 737 count++; 738 } 739 if (++backhand >= maxhand) 740 backhand = 0; 741 } 742 goto loop;
proc[2]はpager,メモリが不足するとwakeupされる
Fronthand.backhandを++しながら一定のpageをスキャンする
fronthand
backhand
maxhand/0
cmap[hand]がcheckの対象
処理:checkpage()
• sys/vm_page.c +774 774 c = &cmap[hand]; 775 if (c->c_lock || c->c_free) 776 return (0); 777 switch (c->c_type) { 779 case CSYS: 780 return (0); 782 case CTEXT: 783 xp = &text[c->c_ndx]; 784 rp = xp->x_caddr; 785 v = tptov(rp, c->c_page); 786 pte = tptopte(rp, c->c_page); 787 break; 789 case CDATA: 790 case CSTACK: 791 rp = &proc[c->c_ndx]; 792 while (rp->p_flag & SNOVM) 793 rp = rp->p_xlink; 794 xp = rp->p_textp; 795 if (c->c_type == CDATA) {
796 v = dptov(rp, c->c_page); 797 pte = dptopte(rp, c->c_page); 798 } else { 799 v = sptov(rp, c->c_page); 800 pte = sptopte(rp, c->c_page); 801 } 802 break; 803 }
cmapのc_typeに用途が記録
実メモリcmap[hand]が参照しているpteを特定
プロセス空間と物理メモリの関連 cmap[] 物理メモリ proc []
c_ndx
c_ndx
p_tsize p_p0br
c_page
cmap[n]
proc[m]
User stack
User Text User Data
0x4000.000
0x0000.000
0x8000.000
P0
P1
#define dptov(p, i) ((unsigned)(stoc(ctos((p)->p_tsize)) + (i)))
#define dptopte(p, i) ((p)->p_p0br + ((p)->p_tsize + (i)))
pte[]
処理:checkpage()
• sys/vm_page.c +813 813 if (pte->pg_v) { 814 if (whichhand == BACK) 815 return(0); 816 pte->pg_v = 0; 817 if (anycl(pte, pg_m)) 818 pte->pg_m = 1; 819 distcl(pte); 820 if (c->c_type == CTEXT) 821 distpte(xp, (unsigned)vtotp(rp, v), pte); 822 if ((rp->p_flag & (SSEQL|SUANOM)) == 0 && 823 rp->p_rssize <= rp->p_maxrss) 824 return (0); 825 }
処理:checkpage()
• sys/vm_page.c +850 850 if (dirtycl(pte)) { 856 if (rp->p_flag & (SLOCK|SWEXIT)) 857 return (0); 858 /* 859 * Limit pushes to avoid saturating 860 * pageout device. 861 */ 862 if (pushes > maxpgio / RATETOSCHEDPAGING) 863 return (0); 864 pushes++; 882 loop2: 883 (void) splbio(); 884 if (bclnlist != NULL) { 885 (void) spl0(); 886 cleanup(); 887 goto loop2; 888 } 889 if (bswlist.av_forw == NULL) { 890 bswlist.b_flags |= B_WANTED; 891 sleep((caddr_t)&proc[2], PSWP+2); 892 (void) spl0(); 900 goto top;
901 } 902 (void) spl0(); 904 MLOCK(c); 905 uaccess(rp, Pushmap, &pushutl); 909 pte->pg_m = 0; 910 distcl(pte); 911 if (c->c_type == CTEXT) { 912 xp->x_poip++; 913 distpte(xp, (unsigned)vtotp(rp, v), pte); 914 } else 915 rp->p_poip++; 916 v = kluster(rp, v, pte, B_WRITE, &klsize, klout, (daddr_t)0); 917 if (klsize == 0) 918 panic("pageout klsize"); 919 daddr = vtod(rp, v, &pushutl.u_dmap, &pushutl.u_smap); 920 (void)swap(rp, daddr, ptob(v), klsize * ctob(CLSIZE), 921 B_WRITE, B_DIRTY, swapdev, pte->pg_pfnum); 929 return (1); /* well, it'll be free soon */ 930 931 }
アドレス変換例外
• machine/trap.c 58 trap(sp, type, code, pc, psl) 59 int sp, type; 60 unsigned code; 61 int pc, psl; 73 switch (type) { 75 default: 76 printf("trap type %d, code = %x, pc = %x¥n", type, code, pc); 77 type &= ~USER; 78 if ((unsigned)type < TRAP_TYPES) 79 panic(trap_type[type]); 80 panic("trap"); 82 case T_PROTFLT+USER: /* protection fault */ 83 i = SIGBUS; 84 break; 119 120 case T_PAGEFLT: /* allow page faults in kernel mode */ 121 case T_PAGEFLT+USER: /* page fault */ 122 i = u.u_error; 123 pagein(code, 0); 124 u.u_error = i; 125 if (type == T_PAGEFLT) 126 return; 127 goto out;
処理:pagein()
• sys/vm_page.c
68 pagein(virtaddr, dlyu) 100 vsave = v = clbase(btop(virtaddr)); 101 p = u.u_procp; 108 pte = vtopte(p, v); 198 bnswap = bncache = bn = vtod(p, v, &u.u_dmap, &u.u_smap); 199 dev = swapdev; 219 opte = *pte; 311 pte->pg_prot = opte.pg_prot; 314 distcl(pte); 377 /* 378 * Fill from swap area. Try to find adjacent 379 * pages to bring in also. 380 */ 381 v = kluster(p, v, pte, B_READ, &klsize, 382 (type == CTEXT) ? kltxt : 383 ((p->p_flag & SSEQL) ? klseql : klin), bn); 384 splx(sk); 385 /* THIS COULD BE COMPUTED INCREMENTALLY... */ 386 bncache = bn = vtod(p, v, &u.u_dmap, &u.u_smap);
387 } 388 389 distcl(pte); 390 swerror = swap(p, bn, ptob(v), klsize * ctob(CLSIZE), 391 B_READ, B_PGIN, dev, 0);
!NOTICE!
第三世代UNIX
• Mach由来の仮想記憶に置き換え
– 仮想記憶は • f(vnode, offset) → (p, vaddr)
• f(p, vaddr) → (vnode,offset)
– 広範囲に影響 • 共用テキスト
• 共有メモリ
• バッファキャッシュ
• Demand loading
• ForkのCopy On Write化
• Linux,Solaris,BSDで実装は異なるが、考え方は同じ