2011.06.11 v7から始めるunix まとめ

1,371 views

Published on

Published in: Education
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,371
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

2011.06.11 v7から始めるunix まとめ

  1. 1. V7から始めるUNIX まとめ予備知識であるCPUアーキテクチャのほん のさわりを説明したあと、本題に入ります
  2. 2. 予備知識• コンピュータ・アーキテクチャについて – 低レイヤのプログラムの理解に欠かせない – 理想を言えば、網羅的に知っておくことが好ましい • ハードとソフトは両輪だから – ただし、技術の範囲が広大すぎて、とてもここでは扱いき れない – 疑問が出た都度、身近な専門家に声をかけて聞くと、きっ と親切に教えてくれる!←学校か職場の専門家が誰か、 把握しておこう! • 私も知っている範囲で答えます – ここではCPUの基本構成、マシン語から演算器で実行さ れる手順にフォーカスして、次ページ以降で少しだけさわ る 2
  3. 3. CPUの基本構成、超ざっくり• 参考情報※の本文が、非 常に素晴らしいので、そち らに詳細の解説はゆずり ます – CPUは大きく、演算部と制 御部から構成されます – CPUにはメモリが接続され ています – ROMには、コンパイラに よってプログラムから変換 され、CPUが実行可能な マシン語に変換されたデー タが入っています※参考:http://homepage2.nifty.com/ttoyoshima/Computer/Config.htm 3
  4. 4. マシン語• プログラム言語はコンパイラによってCPUが直接理 解し実行できるマシン語に変換される• マシン語は、CPUで処理される(命令パイプライン)• マシン語の例参考情報http://www.geocities.jp/mori_imms/asmDetail.htmlhttp://msyk.net/keio/JavaBook/ch1.html 4
  5. 5. 命令パイプライン(1/2)• 汎用パイプラインについて説明する• 次ページの図は、一般化した次のような4段のパイプライン を示している。• フェッチ (Fetch):メモリから機械語を取り出してくる動作• デコード (Decode):機械語を解読し、制御信号を送る• 実行 (Execute):実行する• ライトバック (Write-back):実行結果をファイルかメモリに書き 込む• 次ページの図の上にある灰色の矩形には実行を待っている 命令が並んでいる。下の灰色の矩形には実行が完了した命 令が並んでいる。真ん中の矩形がパイプラインを表している。• 実行は次ページのようになる。参考:Wikipedia 5
  6. 6. 命令パイプライン(2/2) 説明0 4つの命令が実行されるのを待っている。1 緑の命令をメモリからフェッチする。 緑の命令をデコードする。2 紫の命令をメモリからフェッチする。 緑の命令を実行する(実際の命令処理を行う)3 紫の命令をデコードする。 青の命令をフェッチする。 緑の命令の結果をレジスタファイルかメモリに書き込む 紫の命令を実行する。4 青の命令をデコードする。 赤の命令をフェッチする。 緑の命令は完了した。 紫の命令の結果を書き込む。5 青の命令を実行する。 赤の命令をデコードする。 紫の命令は完了した。6 青の命令の結果を書き込む。 赤の命令を実行する。 青の命令は完了した。7 赤の命令の結果を書き込む。8 赤の命令は完了した。9 全命令を実行した。 6
  7. 7. • 示した例は、一般的な例です• 実際はもっと複雑で、いろいろ工夫されてい ます• 気に入っている、または気になるチップの アーキテクチャを調べていくと、楽しいです – チップメーカーは大抵、開発者向けのホワイト ペーパーを出しています – わからない専門用語は、専門家に聞こう! • 私も知っている範囲で答えます 7
  8. 8. V7の理解(1/2)• 現在のLinuxの原点はV7 – V7→BSD→Linux• V7を読み解くことで、原理の理解を進めることを 目的とする• 今回は、V7の理解の一環として、まず root/h/user.hとroot/sys/slp.cの実装を読み解い ていく – slp.cはスワッピングについて記載されており、プロセ ス制御の基本です• 若干「わかっているつもり」な人向け 参照したソースコード: http://www.tamacom.com/tour/kernel/unix/ 8
  9. 9. V7の理解(2/2)• V7ではハードが限定されていたため、可搬 性について考慮していない – DECのPDP-11などで動作した• 歴史についての参考資料 http://ja.wikipedia.org/wiki/Version_7_Unix• ソースコードは簡単シンプル、注釈が丁寧で 読みやすいです… – ただしK&Rで書かれているっぽい 9
  10. 10. user.h(1/5) 冒頭の注釈 (注釈のまんま) ↓左端の数値は行番号 ・user構造体を定義している ・プロセスあたり1つ持つ 1 /* ・1プロセスあたりの全データであ 2 * The user structure. る 3 * One allocated per process. ・プロセスがスワップしている間は、 4 * Contains all per process data 参照しなくてよい ・ユーザのブロック長はUSIZE×64 5 * that doesnt need to be referenced でこれはスタックポインタ用に使わ 6 * while the process is swapped. れる 7 * The user block is USIZE*64 bytes ・仮想カーネルのデータ長は 8 * long; resides at virtual kernel 140000 (ユーザ毎のシステムス 9 * loc 140000; contains the system タック含む)10 * stack per user; is cross referenced・同じプロセスの中では、proc構造 と相互参照の関係にある11 * with the proc structure for the12 * same process.13 */ 10
  11. 11. 1415 #define EXCLOSE 01 user.h(2/5) 1617 struct user18 {19 label_t u_rsav; /* save info when exchanging stacks */20 int u_fper; /* FP error register */ 退避時にスタックポインタを格納21 int u_fpsaved; /* FP regs saved for this proc */22 struct {23 int u_fpsr; /* FP status register */24 double u_fpregs[6]; /* FP registers */ ファイルI/O用の変数25 } u_fps;26 char u_segflg; /* IO flag: 0:user D; 1:system; 2:user I */ファイルI/Oの属性等27 char u_error; /* return error code */ Dはデータ、I、は命令28 short u_uid; /* effective user id */ systemはカーネルに相当29 short u_gid; /* effective group id */ userはプロセスユーザと同義30 short u_ruid; /* real user id */31 short u_rgid; /* real group id */ proc構造体ポインタ32 struct proc *u_procp; /* pointer to proc structure */33 int *u_ap; /* pointer to arglist */ システムコールの引数リスト34 union { /* syscall return values */35 struct {36 int r_val1; システムコールの戻り値37 int r_val2; 例えばLinuxのコマンド38 }; ls –laとかで得られる39 off_t r_off; 一覧のデータを格納40 time_t r_time; 1141 } u_r; しておく
  12. 12. user.h(3/5)42 caddr_t u_base; /* base address for IO */ プロセスユーザのI/Oの開始ア43 unsigned int u_count; /* bytes remaining for IO */44 off_t u_offset; ドレス、オフセット、どこのディ /* offset in file for IO */45 struct inode *u_cdir; レクトリにあるか、パス名(この /* pointer to inode of current directory */46 struct inode *u_rdir; ころは物理アドレス空間を直 /* root directory of current process */47 char u_dbuf[DIRSIZ]; 接扱っていた?) /* current pathname component */48 caddr_t u_dirp; /* pathname pointer */49 struct direct u_dent; /* current directory entry */50 struct inode *u_pdir; /* inode of parent directory of dirp */51 int u_uisa[16]; /* prototype of segmentation addresses */52 int u_uisd[16]; ファイルI/O /* prototype of segmentation descriptors */53 struct file *u_ofile[NOFILE]; /* pointers to file structures of open files * (ちょっと詳細がわからない)54 char u_pofile[NOFILE]; /* per-process flags of open files */55 int u_arg[5]; /* arguments to current system call */56 unsigned u_tsize; /* text size (clicks) */57 unsigned u_dsize; /* data size (clicks) */ 現在のシステムコール戻り値58 unsigned u_ssize; /* stack size (clicks) */ テキスト(プログラム)、データ59 label_t u_qsav; /* label variable for quits and interrupts */60 label_t u_ssav; /* label variable for swapping */ (固定値)、スタック(call時のP61 int u_signal[NSIG]; /* disposition of signals */ C値の格納等)のサイズ、ス62 time_t u_utime; /* this process user time */ ワップや退避時のラベル値63 time_t u_stime; /* this process system time */64 time_t u_cutime; /* sum of childs utimes */ システムコールの戻り値65 time_t u_cstime; /* sum of childs stimes */ プロセスおよび子プロセスの66 int *u_ar0; /* address of users saved R0 */ CPU使用時間など 12
  13. 13. user.h(4/5)75 struct tty *u_ttyp; /* controlling ttytty(キャラクタ端末)制御用ポイ pointer */76 dev_t u_ttyd; /* controlling tty ンタとデバイス制御用情報 dev */77 struct { /* header of executable file */78 int ux_mag; /* magic number */79 unsigned ux_tsize; /* text size */80 unsigned ux_dsize; /* data size */ これはわからない…81 unsigned ux_bsize; /* bss size */82 unsigned ux_ssize; /* symbol table size */ 実行するプログラムの83 unsigned ux_entloc; /* entry location */ 情報(予備?)か何かか84 unsigned ux_unused;85 unsigned ux_relflg;86 } u_exdata;87 char u_comm[DIRSIZ];88 time_t u_start;89 char u_acflag;90 short u_fpflag; /* unused now, will be later */ 予備用(互換性を取るため?)91 short u_cmask; /* mask for file creation */92 int u_stack[1];93 /* kernel stack per user94 * extends from u + USIZE*6495 * backward not to reach here96 */97 }; 98 13
  14. 14. user.h(5/5) エラーコードの定義101 /* u_error codes */ 118 #define EEXIST 17102 #define EPERM 1 119 #define EXDEV 18103 #define ENOENT 2 120 #define ENODEV 19104 #define ESRCH 3 121 #define ENOTDIR 20105 #define EINTR 4 122 #define EISDIR 21106 #define EIO 5 123 #define EINVAL 22107 #define ENXIO 6 124 #define ENFILE 23108 #define E2BIG 7 125 #define EMFILE 24109 #define ENOEXEC 8 126 #define ENOTTY 25110 #define EBADF 9 127 #define ETXTBSY 26111 #define ECHILD 10 128 #define EFBIG 27112 #define EAGAIN 11 129 #define ENOSPC 28113 #define ENOMEM 12 130 #define ESPIPE 29114 #define EACCES 13 131 #define EROFS 30115 #define EFAULT 14 132 #define EMLINK 31116 #define ENOTBLK 15 133 #define EPIPE 32117 #define EBUSY 16 134 #define EDOM 33 135 #define ERANGE 34 14
  15. 15. slp.c• プロセスのスケジューリングを行うための関数群• 基本的にキューと、設定された優先度で管理するようだ• 以下の関数について定義している(★は親関数っぽいもの) – sleep:プロセスの実行を中断し、プロセッサを明け渡す – wakeup:sleep中のプロセスを起こす – setrun:プロセスが実行できるようにする(スワップインできるようにす る) – setpri:プロセスユーザの優先度をセットする関数 – ★sched:スケジューリング(スワッピング)のメイン関数 – swapin:プロセスをスワップインさせる関数 – qswitch:プロセスキューの設定を行う関数 – ★switch:CPUのリスケジュール(資源の再割り当て)を行う場合に呼 び出す – newproc:新しいプロセスを立ち上げる(fork?) – expand:データとスタックのサイズを変える – setrq: 15
  16. 16. 202 sched()203 {204 register struct proc *rp, *p; sched(1/3)205 register outage, inage; ところどころに出てくる、206207 int maxsize; 優先度を制御する関数。spl*の*が208 /* 大きいほど優先度が高く、6が最高。209 * find user to swap in;210 * of users ready, select one out longest 他から割りこみが入って状態が211 */ 変わらないようにしたい場合にこうする。212213 loop: ここでは最高優先度まで上げて以降の214 spl6(); 操作を行っている215 outage = -20000;216 for (rp = &proc[0]; rp < &proc[NPROC]; rp++)217 if (rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 && プロセスの状態を総なめして218 rp->p_time - (rp->p_nice-NZERO)*8 > outage) {219 p = rp; ステータスが実行可能状態で220 outage = rp->p_time - (rp->p_nice-NZERO)*8; 優先度が一番高いものを検出221 }222 /* (スワップイン候補を探す)223 * If there is no one there, wait.224 */225 if (outage == -20000) {226 runout++; 上記条件にあうプロセスが検出できなけれ227 sleep((caddr_t)&runout, PSWP);228 goto loop; ば、sleepを呼び出して待つ229 }230 spl0();231232 /* 優先度を最低に戻す233 * See if there is core for that process;234 * if so, swap it in.235 */236237 if (swapin(p)) プロセスに割り当てられる資源(メモリなど)238 goto loop; があればスワップインする 16
  17. 17. 240 /* sched(2/3)241 * none found.242 * look around for core.243 * Select the largest of those sleeping244 * at bad priority; if none, select the oldest.245 */246247 spl6();248 p = NULL;249 maxsize = -1;250 inage = -1;251 for (rp = &proc[0]; rp < &proc[NPROC]; rp++) {252 if (rp->p_stat==SZOMB253 || (rp->p_flag&(SSYS|SLOCK|SULOCK|SLOAD))!=SLOAD)254 continue; CPUに割り当255 if (rp->p_textp && rp->p_textp->x_flag&XLOCK)256 continue; てる候補と資257 if (rp->p_stat==SSLEEP&&rp->p_pri>=PZERO || rp->p_stat==SSTOP) { 源がない状258 if (maxsize < rp->p_size) {259 p = rp; 態。一番最初260 maxsize = rp->p_size; にsleepしたプ261 }262 } else if (maxsize<0 && (rp->p_stat==SRUN||rp->p_stat==SSLEEP)) { ロセスを総な263 if (rp->p_time+rp->p_nice-NZERO > inage) { めして検出す264 p = rp;265 inage = rp->p_time+rp->p_nice-NZERO; る(優先的に266267 } } スワップさせ268 } るため)269 spl0();270 /* 17
  18. 18. sched(3/3)270 /*271 * Swap found user out if sleeping at bad pri,272 * or if he has spent at least 2 seconds in core and273 * the swapped-out process has spent at least 3 seconds out.274 * Otherwise wait a bit and try again.275 */276 if (maxsize>=0 || (outage>=3 && inage>=2)) {277 p->p_flag &= ~SLOAD; CPUに割り当てられた時間278 xswap(p, 1, 0); または、低い優先度でsleep279 goto loop;280 } していたら、スワップを行う281 spl6(); (理由がちょっと不明)282 runin++;283 sleep((caddr_t)&runin, PSWP);284 goto loop;285 }286 18
  19. 19. 347 swtch()348 {349350352 register n; register struct proc *p, *q, *pp, *pq; /* switch(1/2)353 * If not the idle process, resume the idle process.354 */355 if (u.u_procp != &proc[0]) {356 if (save(u.u_rsav)) {357 sureg();358 return;359 } スタックポインタか?を保存360 if (u.u_fpsaved==0) { や取り出している361 savfp(&u.u_fps);362 u.u_fpsaved = 1;363 }364 resume(proc[0].p_addr, u.u_qsav);365 }366 /*367 * The first save returns nonzero when proc 0 is resumed368 * by another process (above); then the second is not done369 * and the process-search loop is entered.370 *371 * The first save returns 0 when swtch is called in proc 0372 * from sched(). The second save returns 0 immediately, so373 * in this case too the process-search loop is entered.374 * Thus when proc 0 is awakened by being made runnable, it will375 * find itself and resume itself at rsav, and return to sched().376 */377 if (save(u.u_qsav)==0 && save(u.u_rsav)) 19
  20. 20. 379 loop:380 spl6();381 runrun = 0;382383384 pp = NULL; q = NULL; n = 128; switch(2/2)385 /*386 * Search for highest-priority runnable process387 */388 for(p=runq; p!=NULL; p=p->p_link) {389 if((p->p_stat==SRUN) && (p->p_flag&SLOAD)) {390 if(p->p_pri < n) {391 pp = p;392 pq = q;393 n = p->p_pri;394 }395 }396 q = p;397 }398 /*399 * If no process is runnable, idle.400 */401 p = pp;402 if(p == NULL) {403 idle();404 goto loop;405 }406 q = pq;407 if(q == NULL)408 runq = p->p_link;409 else410 q->p_link = p->p_link;411 curpri = n;412 spl0();413 /*414 * The rsav (ssav) contents are interpreted in the new address space415 */416 n = p->p_flag&SSWAP;417 p->p_flag &= ~SSWAP;418 resume(p->p_addr, n? u.u_ssav: u.u_rsav); 20419 }
  21. 21. 292 swapin(p)293 register struct proc *p; swapin294 {295 register struct text *xp;296 register int a; ここ少し見たがまだ不明。297 int x;298299 if ((a = malloc(coremap, p->p_size)) == NULL)300 return(0);301 if (xp = p->p_textp) {302 xlock(xp);303 if (xp->x_ccount==0) {304 if ((x = malloc(coremap, xp->x_size)) == NULL) {305 xunlock(xp);306 mfree(coremap, p->p_size, a);307 return(0);308 }309 xp->x_caddr = x;310 if ((xp->x_flag&XLOAD)==0)311 swap(xp->x_daddr,x,xp->x_size,B_READ);312 }313 xp->x_ccount++;314 xunlock(xp);315 }316 swap(p->p_addr, a, p->p_size, B_READ);317 mfree(swapmap, ctod(p->p_size), p->p_addr);318 p->p_addr = a;319 p->p_flag |= SLOAD;320 p->p_time = 0;321 return(1); 21

×