name: computer-basic class: title, smokescreen, shelf, no-footer # デバイスドライバ <div class=footnote> <small> <small> (脚注) 前節「競合状態」の続き(「競合状態」で使うテクニックの説明後、やっとデバドラの話ができます) </small> </small> </div> --- class: img-right,compact # CPUとデバイスの接続形態の代表例 ![height240px](images/cpu_pdp_cpu.png) ![height240px](images/cpu_8086_cpu.png) - memory mapped (図上) - メモリ上にデバイスアクセス用の場所がある、例:キーボードは0x220 - ポインタ操作でデバイスが制御できるため、 デバイスだけ特別あつかいをしない統一感のあるコードの書き方ができて綺麗 - IO mappedもしくは port mapped (図下) - メモリ空間は全てプログラム用、デバイスは別枠 (設計思想として、PCはメモリが小さいためmemory-mappedでは**もったいない**ためらしい) - IntelならIOポートというメモリ空間とは別にある回路を経由してデバイスへアクセス --- class: img-right,compact # HDDへの読み書きの全体像(半分は復習) <div class=footnote> <small> <small> (脚注) いろいろはしょってます </small> </small> </div> ![](images/hdd.png) 1. エディタでa.cを読みたい(と命令) 2. **システムコール**をかける(カーネルモードへ移行、エディタは一時中断) 3. HDDの**デバイスドライバ**(以下デバドラ) 4. デバドラからHDDへ命令を送り、処理(READ)が終わるまでデバドラは休止 5. HDDが依頼された処理を終えると**ハードウエア割り込み**をかけてカーネルに合図 6. デバドラに戻り、 kernelは読みこんだa.cの中身をエディタに返す準備をする 7. システムコールから復帰(ユーザモードへ戻り、エディタプロセス再開) 8. エディタでa.cが見えるようになる --- class: compact # HDDデバイスドライバの擬似コード例(前頁の4.部分) <small> - デバドラの詳細はハードウエアごとに大きく異なります。 これはDEC PDP-11のRK05というHDDの例でmemory mappedです。 コマンドを書き込んだ瞬間に動作が始まります ``` ポインタ *p; // 2バイト幅の変数へのポインタなので、p--すると2バイト減少することに注意 ポインタpをHDDを制御できるアドレス(65280〜65291番地間)の一番上(65290)に合わせる *p-- = HDD上に書き込む場所の指定(セクタの番号) *p-- = メモリ上のバッファのアドレス *p-- = 読み書きするバイト数 *p = コマンド // memory mappedの例: メモリ上の配置は以下のとおりで、アドレスは十進数表記に変更済 65290 HDD上の場所の指定(セクタの番号) 65288 メモリ上のバッファのアドレス(読み書きする場所、読み書きの方向はコマンド依存) 65286 読み書きするバイト数 65284 コマンド (読み/書きの指定) 65282 デバイス側でエラーの理由を書き込む場所, READ ONLY 65280 HDDのステータス(Drive Status Register, READ ONLY) ``` </small> --- class: img-right,compact # 例: 時計の動作 <div class=footnote> <small> <small> (脚注1) いろいろはしょってます (脚注2) コンピュータの時計は内部で「起動時の時刻 + 1/100 * tick」で推定しています。 当然すこしずつ狂うので、 Network Time Protocolで微修正をかけつつtickカウントをとっています (脚注3) 3.ではその他の処理をしてもok。 UNIX第6版では、 プロセス優先度の再計算や、 Nミリ秒後に実行予約された処理がないか?の確認などをしています (脚注4) カーネルが他の処理を実行中にクロックからの割り込みをうけることもありえます。 その場合カーネルが一時中断->時計の処理->元の処理へ復帰 </small> </small> </div> ![](images/clock.png) <small> 1. クロックを刻んでいる回路からハードウエア割り込み (例: 1/100秒に一回) 2. 実行中のユーザプロセスはプリエンプトされ一時中断、カーネルへ移行 3. 時計のデバドラを実行 - カウント(tick変数)を +1 する 4. ユーザプロセスへ復帰 </small>