雑記

いち情報系大学生

OS学習メモ5(30日OS自作入門5日目)

張り切って5日目。
前回まででそれっぽい画面が出来たので、今回は文字を表示させてみたい。
f:id:unyamahiro:20180504140000p:plain
…まぁ本の通りやったら表示できますよそりゃぁね(笑)。でもそれだけじゃぁつまらないので、自分でも何かかいてみようと思った。考えた結果フレーミーを描くことにした。

それでっ \バン/(気力の音)
f:id:unyamahiro:20180504140131p:plain

とりあえずこんな感じになったのはいいが、本に書いてあるやり方では35x26マスのフォントなんて書けそうにないのでここからさらに…
f:id:unyamahiro:20180504140236p:plain
8x26に分割する。

OSASKのフォントファイルhankaku.txtを見たら、最後の方が使われていなかったので、ここに作ったやつを自分で手入力をして(8x26)、bootpack.cを(だいぶ無茶して)変えて…
f:id:unyamahiro:20180504140403p:plain
huremi関数を作って表示するところまで出来ました
(ここまで色々な格闘があった)

とまぁ3時間くらいかけてフォントを用意するところから自分でフレーミーが描けたわけです。こうして自分で作ってみて「英字のフォント、さらりと用意されているけどこれ用意するのとか大変だったんだろうなぁ」と思いました。思わされました(笑)。OSASKフォントを作った方に敬意を表して今後有り難〜く使わせてもらおうと思ったのでした(笑)。

さてさて。次はマウスを動かそうとのこと。
ここでGDTとIDTの初期化の話が出てくるが、その前にセグメンテーションの話。セグメンテーションは、ざっくりいうと「メモリを自由に切り分けて独立させること」で、これによって複数のプログラムをメモリ上でぶつからないように配置させたり、他のプログラムに不正にアクセスできないようにすることができる。こうして切り分けた一つひとつをセグメントと呼ぶが、このセグメントを表すには「セグメントの大きさ」「セグメントがどの番地から始まるか」「セグメントの管理用属性」の3つの情報が必要になり、CPUはこれらを64ビットのデータで表している。とは言っても、セグメントを指定するレジスタは16ビットしか無い。困った。

そこで別の方法を使う。
4日目でパレットを設定したときのように、セグメントセレクタと呼ばれる番号をそれぞれのセグメントとあらかじめ対応づけておいて、使う時は、このセグメントセレクタをセグメントレジスタで指定して使う。13ビット分(8192個)のセグメントを指定することができ、肝心のそれぞれのセグメント情報はセグメントディスクリプタテーブル(GDT)というやつに格納してメモリにおいておく。ごちゃごちゃしてきたので図で整理するとこんな感じ(セグメント番号って書いてあるのが上の説明でいうセグメントセレクタのこと)
f:id:unyamahiro:20180504163812p:plain
ちなみに、メモリの中にあるセグメントディスクリプタテーブルの先頭番地や有効設定個数などはGDTR(セグメントディスクリプタテーブルレジスタ)というレジスタに格納される。長くなったけど、ここまでがGDTの話。

IDTは割り込みディスクリプタテーブルのこと。(以前MINIX本で勉強して記事にもまとめた気がするけど)CPUでは、外部からの入力あるいはめったに起きない内部トラブルが起こると、その時点での処理を一旦中断して別の処理に切り替える機能があり、これを割り込みという。ここでの目的はマウスを動かすことで、マウスを使うには割り込みを使えるようにならなくてはならず、そのためにはIDTを設定する必要があるという流れ。IDTは、割り込み番号0-255に対して「割り込み番号123が発生したらOX関数を呼び出す」みたいな設定の表(GDTに似ている)。IDT設定のためにはセグメントの設定がきちんと完了していないといけないのでGDTを設定した。

コードの関係ありそうな所を見てみる(サンプルコピー)

struct SEGMENT_DESCRIPTOR {
	short limit_low, base_low;
	char base_mid, access_right;
	char limit_high, base_high;
};

struct GATE_DESCRIPTOR {
	short offset_low, selector;
	char dw_count, access_right;
	short offset_high;
};

void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

void init_gdtidt(void)
{
	struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
	struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;
	int i;

	for (i = 0; i < 8192; i++) {
		set_segmdesc(gdt + i, 0, 0, 0);
	}
	set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
	set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);
	load_gdtr(0xffff, 0x00270000);

	for (i = 0; i < 256; i++) {
		set_gatedesc(idt + i, 0, 0, 0);
	}
	load_idtr(0x7ff, 0x0026f800);

	return;
}

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
	if (limit > 0xfffff) {
		ar |= 0x8000;
		limit /= 0x1000;
	}

	sd->limit_low    = limit & 0xffff;
	sd->base_low     = base & 0xffff;
	sd->base_mid     = (base >> 16) & 0xff;
	sd->access_right = ar & 0xff;
	sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
	sd->base_high    = (base >> 24) & 0xff;
	return;
}

void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
	gd->offset_low   = offset & 0xffff;
	gd->selector     = selector;
	gd->dw_count     = (ar >> 8) & 0xff;
	gd->access_right = ar & 0xff;
	gd->offset_high  = (offset >> 16) & 0xffff;
	return;
}

まず気になるのが

for (i = 0; i < 8192; i++) {
	set_segmdesc(gdt + i, 0, 0, 0);
}

ここでの変数gdtにはセグメントディスクリプタの先頭アドレスが入っている。整数iを加えていくことで(ポインタなので番地は8ずつ増える)、全てのセグメントに対して「セグメントの大きさ0」と「番地0」と「アクセス権の属性0」を設定している。

次の行からは

set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);
load_gdtr(0xffff, 0x00270000);

セグメント番号1番とセグメント番号2番に対して設定。CではGDTRに代入することが出来ないのでload_gdtr関数を呼んでアセンブラの力を借りて設定する。IDTも同じようなことをしている。

これにて5日目終了です。
さて、6日目に入ります。

OS学習メモ4(30日OS自作入門4日目)

※OS本の前置きの方にある「これは教材OSだから自由に使って下さい」のお言葉に甘えてこの記事でもコードそのまま載せさせてもらっています。本当に有り難く思っています。この場を借りてお礼申し上げます。


前回は真っ黒画面を出力したので今回は何か書きたい。
というわけで4日目ではそんな関数を作っていく。

_write_mem8:	; void write_mem8(int addr, int data);
		MOV		ECX,[ESP+4]		
		MOV		AL,[ESP+8]		
		MOV		[ECX],AL
		RET

Cからアセンブラの関数を呼び出せるようにするには、ラベルのところに”_関数名”と書く。ここではwrite_mem8関数としてCから呼ぶことができる。Cでこの関数を呼び出した時に指定した引数はスタックに格納されているので、それらをECXとALレジスタに一旦読み込んでから、MOV命令を使ってメモリに値を書き込んでいる。

これを使ってC言語の方を書くと

void write_mem8(int addr, int data);
void HariMain(void)
{
	int i;
	for (i = 0xa0000; i <= 0xaffff; i++) {
        write_mem8(i, 15);
        }
    	for (;;) {
		io_hlt();
	}
}

これはVRAM全部に色番号15番(白)を書きこんでいて(背景同化してる)、
f:id:unyamahiro:20180503232602p:plain
当然だけど15という値を変更したら色んな色になる。
f:id:unyamahiro:20180503232727p:plain

これを利用して、書きこむ色を少し工夫することでこんな模様も書ける。
f:id:unyamahiro:20180503232755p:plain

…。まぁ、別にメモリに書きこむだけならばわざわざアセンブラを使わずともポインタでも良いわけで、それが↓

void HariMain(void)
{
	int i;
	char *p;

	for (i = 0xa0000; i <= 0xaffff; i++) {
		p = i; 
		*p = i & 0x0f;
	}
}

さて、今使っている8ビットカラーモードだと255通りの色指定までしか出来ない。普通のPCとかがRGBで0x000000から0xffffffまで指定とかしていることを考えても、これだとあまりにもバリエーションが少ないので、ちゃんと好きな色を使えるようにする(最大256色を設定できるが今回はそんなに使わないので16色)

#000000:黒,#ff0000:明るい赤,....,#008484:暗い水色,#848484暗い灰

void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);

void HariMain(void)
{
	int i; 
	char *p; 
	init_palette();
	p = (char *) 0xa0000; 
	for (i = 0; i <= 0xffff; i++) {
		p[i] = i & 0x0f;
	}

}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,
		0xff, 0x00, 0x00,	
             // 略
		0x00, 0x84, 0x84,
		0x84, 0x84, 0x84	
	};
	set_palette(0, 15, table_rgb);
	return;
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	
	io_cli(); 					
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	
	return;
}

やることはシンプル。まずCLI。次に0x03c8にパレット番号(0-15)を書き込んで、続いてR,G,Bをこの順に0x03c9に書きこむ。これをパレットの色の種類数だけ(ここでは16回)繰り返したら、最後に(必要に応じて)STIをする。

CLISTI
CLI命令は割り込みフラグを0に,STIは1にする命令。CPUに割り込み要求が入った時、このフラグが0ならば無視するし1ならば要求を処理する。割り込みの説明はまた今度ということなのでここでもこのくらいにしておく。

ここで登場するEFLAGSについて補足。これはキャリーフラグや割り込みフラグなどの色々なフラグが詰まったレジスタ。割り込みフラグを操作しようと思ったらEFLAGSを読み込むしかない。

こんな感じでサンプルプログラムをそのまんま実行したり四角を増やしたりした。
f:id:unyamahiro:20180503233932p:plain

こんな感じで4日目。
今日は寝よう...楽しかったけど疲れた。

OS学習メモ3(30日OS自作入門2-3日目)

OS学習の流れにのって30日本久々に読んでます。

この記事「OS本を持っていない人にもわかりやすく伝える」という感じではないです(微妙なところですが)。どちらかといえば進捗報告に近いのかなって思ってて、本読めばわかるようなところは説明まるごとすっ飛ばしたりします。時間があればガッツリ書きたかったんですがそうもいかないので…。

一日目 

すっ飛ばし

二日目

新出命令

ORG: 続く命令をどこから格納するか、つまり先頭アドレスを指示する命令。

MOV:Cでいう代入みたいなもの。データをコピーする。

ADD:スペルそのまま、足し算する命令。

CMP:2つのオペランドを比較し、結果をEFLAGSのキャリーフラグ(後述)にセットする。ここではジャンプ命令と組み合わせて条件分岐に使った。

JMP:ジャンプ~。簡単ですね(ここ好きだったのでパクりました)

HLT:CPUを待機状態にさせる命令。

INT:割り込み命令。3日目のところで説明する。

参考:http://softwaretechnique.jp/OS_Development/Tips/IA32_Instructions/A.html

レジスタ

汎用レジスタ(AX/BX/CX/DX)

何に使ってもいいレジスタ。頭文字がABCDになっていて覚えやすいけどこれは偶然で、それぞれAccmulator,Base,Counter,Dataの頭文字を取っている。何に使ってもいいとは言っても、AXは演算に,BXはメモリをの領域指定に,CXはカウンタ変数とかに,DXはI/O操作とかに使われることが一般的。

特殊レジスタ(SP/BP)

用途が限られているレジスタで、それぞれStackとBaseの略。SPはスタック領域の一番上、BPはスタック領域の底を指す。確か後になって使うレジスタだったと思うのでここでの説明は省略。ちなみにここには登場していないがIP(Instruction Pointer)とかもあって、次に実行するアドレスを入れる。

インデックスレジスタ(SI/DI)

SI (DI )は転送命令の転送元(先)を示すレジスタ。それぞれSourceとDestinationの略。

セグメントレジスタ

(後で出てくる)

 

メモリ指定の話

MOV命令で、転送先/元にメモリを指定する場合は[ ]のかっこを使う。この時、かっこの前にBYTEやWORDなどの単語を指定することで、読み書きするデータの大きさも指定できる。

3日目

新出命令

ジャンプ系命令がいくつか

説明を保留してたINT命令

INTは割り込み用の命令。PCにはBIOS(Basic Input Output System)というプログラムがある。BIOSはPC起動時に最初に起動され、OSを起動するよりも前に(マウス,ディスクなどの)入出力装置を使用できる状態にしてくれたり、またそれらを制御する関数を提供している。INT命令はその関数を呼び出すために使う命令で、”INT 0x--”のように数字とセットで使われる。参考:http://oswiki.osask.jp/?(AT)BIOS

ディスク読み込み

AL,CH,CL,CH,DLに値を代入してディスクのどの部分を読み込むかを決定する。順に、処理するセクタ数,シリンダ番号,セクタ番号,ヘッド番号,ドライブ番号となっている。ちょうどよいところに数日前の記事で使った画像があったので再掲します

f:id:unyamahiro:20180503143027j:plainf:id:unyamahiro:20180503143126j:plain

画像にない所を補足すると..".ヘッド番号"は上から磁気ヘッドを当てるか下から当てるかを0/1で指定するもので、"ドライブ番号"は…とりあえず0を指定しておけば良い。

バッファアドレス

メモリのどこに読み込むかを表す番地のこと。これをBXレジスタ(16bit)だけを使って表そうとすると、0-0xffff(およそ64KB)までの領域しか表せないので、もっと多くの番地を指定しようということでセグメントレジスタ(ES/CS/SS/DS/FS/GS)が使われた。ES:BXという表現がまさにそれで、この場合はES*16+BX番地を指定していることになる。

ここまでがIPLの説明。

ここからOS本体に入る

まずHLTをつかったプログラム(haribote.s)を作成して、ディスクイメージに保存する。それをバイナリエディタで観察すると「空の状態のディスクにファイルを保存するとファイル名は0x002600以降に入ること」と「ファイルの中身は0x004200以降に入ること」の2つがわかる.........らしい。

早速自分でも試してみようと思ったんですが、本とは環境が大分違うので結構調べることになりました。結論から言うと「それっぽいことはできたが確信が持てない」って感じです。..まぁとりあえず載せます。

まず準備としてharibote.nasを用意

 fin:
HLT
JMP fin

 アセンブルして実行形式のharihariというのを作った。

nasm -f bin haribote.nas -o harihari

準備ができたらまず「Finder→アプリケーション→ユーティリティ→ディスクユーティリティ」を開いて、空のイメージを作成して(この時「サイズ:1440KB」「パーティション:パーティションマップなし」「フォーマット:MS-DOS(FAT)」に変更する)、作成したディスクイメージの中にHLTのプログラムを普通に置く 

f:id:unyamahiro:20180503145419j:plain

f:id:unyamahiro:20180503153023p:plain

そしたらまた同じようなディスクイメージを、今度は別の場所に作る(画像ではデスクトップに作ってたのでそれ以外の場所に)。作り終えたらディスクユーティリティの画面に戻り、2回目に作ったディスクを選択して「復元→復元元で1回目のディスクイメージ選択」を実行する。そうして出来上がったものをバイナリエディタ(自分は0xED)で開いて中身を見てみた。

f:id:unyamahiro:20180503153446p:plain

f:id:unyamahiro:20180503153454p:plain

たしかにファイル名は0x002600以降にあるなという感じだが、F4 EB FDは0x007A00のところに来てる(っていうか目grepでこれ見つけるのほんと大変だった)。まぁ...理由はわかんないけどそんなに大きな違いとかはなさそう(?)

...ブートセクタの先頭がメモリの0x8000番地だったら...この0x7A00の部分はメモリの0x00FA00に読み込めているということなんだろうか?ならばharibote.nasに"ORG 0x00FA00"を付け加えて、ipl.nasの最後には"JMP 0x00FA00"を付け加えて...。

ってやって、そのちょっと先の方まで読み進めたが、なかなか実行までたどり着けずかれこれ4,5時間は格闘した。そして格闘した結果諦めることにした...ほんとに悔しい。自力では無理だったのでここからは先人が用意してくれた方法を使って先に進みます。

環境を整えて実行し...

f:id:unyamahiro:20180503170825p:plain

おぉ...すごい...ちゃんと動いてる...。

 

今回はここまで。正直めちゃくちゃ疲れました...

OS学習メモ2 (3.1-3.7章)

まとめ2回目。前回の続きで2.5章からまとめようかなって思ったのですが(ここ面白かったんですけど)実装メインの内容だったので、恐らくこれから手を動かしてやっていくうちに何度も後から見るだろうなって思ったので3章から。

3章 入出力

OSの主機能の一つはコンピュータの入出力デバイスを制御すること。デバイスに指令を送って、割り込みを受け取って、そして必要に応じてエラー処理をする。

 3.1.1 入出力デバイス

入出力デバイスは、ブロックデバイスとキャラクタデバイスの2つに分けられる。情報を固定長のブロックに格納するのがブロックデバイスで、それぞれのブロックを他のブロックとは独立して読み書きできるという特徴がある。そして、そんなブロック構造を意識せずに文字列を送受信できるのがキャラクタデバイスである。ただ、この分類はいくつかのデバイスには適合できないものもある。…とは言えこの2つの概念は、OSが入出力に依存しない処理を行うための基礎概念としては十分なもの。

 3.1.2 デバイスコントローラ

入出力装置は一般に電子部品と機械部品から成る。

これらは、多くの設計や製品に使えるよう切り離すことが可能で、電子部品はデバイスコントローラ/アダプタと呼ばれている。機械部品はデバイスそのもののこと。

 

OSは通常、デバイスそのものではなくコントローラに対して処理を行う。

コントローラの仕事は、連続したビット列をバイト列のブロックに変換して、必要に応じてエラー訂正を行うこと。チェックサムを計算してブロックにエラーが無ければ、メインメモリへコピーされる。

3.1.3 メモリマップドI/O

それぞれのコントローラは、CPUと通信するためにいくつかのレジスタを持っている。これらに書き込みを行うことで、OSはデバイスに対しデータの送受信やON/OFFを指定することができる。またレジスタを読み出すことでデバイスが次のコマンドが実行可能かどうかを知ることもできる。メモリマップドI/Oとは、通常のメモリアドレス空間上にI/Oレジスタが共存して、メモリのR/WのCPU命令をI/Oにも使用するというもの。

 

3.1.4 割り込み

制御レジスタは、「出力が完了したか,新しいデータを入力デバイスから受信したか」を判定するためのビットを1つ以上持っており、デバイスが新しいデータの送受信が可能になるまでCPUはこれをチェックする。これは、ポーリング又はビジーウェイトと呼ばれる。

これに加え、多くのコントローラは、コントローラ自身のレジスタが読み書き可能になると、割り込みによってそれをCPUに通知する。これによりCPUは処理が中断され、割り込みハンドラの実行が開始する。

 

3.1.5 DMA(Direct Memory Access)

大きなデータブロックを転送するディスク装置のようなデバイスでは、CPUがI/Oコントローラに対して一度に1バイトずつデータを要求していたのでは時間の無駄である。そこで、ハードウェアを利用した高速な転送を行うためにDMAを使う。


3.2 入出力ソフトウェア

3.2.1 入出力ソフトウェアの目標

入出力ソフトウェアの設計においては、特定のデバイスに限定されずにどんな入出力装置にもアクセスできるプログラムを書くこと、つまりデバイスに依存しないことが重要(ネーミング規定/エラー処理/転送時の同期非同期問題/バッファリング/デバイスの占有と共有 など)

 

入出力ソフトウェアは以下の4つの階層で構成できる

  __________________

  |  ユーザレベルの入出力ソフトウェア   |

  |__________________|

  |デバイスに依存しないOSソフトウェア  |

  |__________________|

  |     デバイスドライバ     |

  |__________________|

  |      割り込みハンドラ     |

  |__________________|

| ̄                        ̄|

|       ( ハードウェア)           | 

|______________________| 

 

3.2.2 割り込みハンドラ

割り込みを意識しなくて済むよう、OSは内部でその処理を隠す必要がある。一番良いのは、入出力が完了して割り込みが発生するまでドライバの入出力を中断してしまうこと。

 

3.2.3 デバイスドライバ

コンピュータシステムは、入出力デバイスを制御するためのデバイス固有のプログラムを必要とする。このデバイスドライバと呼ばれるプログラムは通常デバイスのメーカによって作成される。一般的にデバイスドライバの仕事は、上位のデバイスに依存しない、ソフトウェアからの抽象的な命令を受け付けてその要求を実行すること。

 

3.2.4 デバイスに依存しないOSソフトウェア

デバイスドライバとデバイスに依存しないOSソフトウェアの厳密な境界線はシステムによって異なる。この階層の基本的な機能は、全てのデバイスに共通な入出力機能を実装しユーザレベルのソフトウェアに対して統一したインターフェースを提供すること。

 

3.2.4  ユーザレベルの入出力ソフトウェア

大部分の入出力ソフトウェアはOSの内部にあるが、ユーザプログラムにリンクしてライブラリとなる部分があり、これはカーネル外で動作する。とは言っても全てのユーザレベル入出力ソフトウェアがライブラリ関数で構成されるというわけではなく、マルチプログラミングにおいて占有型の入出力デバイスを実装するときにはスプーリングシステムが用いられることがある。これはデーモンだけがデバイスにアクセスできるようにするというシステムで、ユーザが必要以上に資源を専有して他のプロセスが利用できなくなるということを防ぐ仕組み。

 

3.3 デッドロック

デッドロックとは、「2つ以上のプロセスがお互いの終了を待った結果、処理が先に進まなくなってしまっている状態」のこと。これを防ぐために、OSは、プロセスが特定資源へ排他的にアクセスできることを保証する機能を提供している。

 
3.3.2 デッドロックの現地

Coffmanはデッドロックの発生条件として以下の4つを定めており、これらが全て揃うとデッドロックが発生する。

 

①現時点で、各リソースはたった一つのプロセスに割り当てられている。もしくは利用可能な状態になっているかのどちらかであること

②現時点で先に獲得したリソースがあっても、さらに新しいリソースを要求できること

③プロセスに割り当てられているリソースは、そのプロセスが明示的に解放しなければ強制的に取り上げられることがない

④2つ以上のプロセスが環状につながっていて、それぞれが次のプロセスが保有するリソースを待っている状態

 

続く以下は回避策

 
3.3,3 ダチョウアルゴリズム(ostrich algorithm)

デッドロックが発生する可能性は、実は非常に小さい。そんなデッドロックの問題を解消するためにわざわざ制約をかけたりするよりも(ダチョウが砂の中に頭を突っ込んで見ないふりをするかのように)問題を無視してしまえという方式(名前のセンスが好き)

 

3.3.4 検知して回復する方法

リソースの要求や解放が行われるたびにリソースグラフ(有向グラフを使ってリソースの状態を表したもの)を更新して、グラフがリング状になっていないことをチェックする。もしなっていた場合はリングの中のプロセスのうち1つを削除する

 

3.3.5 デッドロック防止

そもそも構造的にデッドロックが起きないように出来ないかを考える。
先に挙げたCoffmanの発生条件を解消するような制約を考える

①1つのプロセスに排他的に割り当てるリソースがなければデッドロックは起きないため、スプーリングを用いてこれを解決する。…と言っても、全てのデバイスでスプーリングができるわけではない。

②リソースを保有しているプロセスが他のリソースを待ち合わせないようにするという方法。しかし、この方法はリソースを最適に使われない等の問題点がある。

③プリンタの出力最中で強制的にリソースを取り上げるのが困難であるように、3つ目の条件からのアプローチは難しい

④これにはいくつかの方法がある。1つ目は「プロセスには一度に1つのリソースしか使用を認めない」というルールにすること。ただし、プロセスが巨大なファイルをテープ装置からプリンタにコピーする場合がある場合この方法は成り立たない。2つ目の方法は、全てのリソース全体に通し番号を付ける方法。プロセスは必要とするリソースを要求できるが、必ず全てのリソースを番号順に要求しなければいけないというもの。

 

3.3.6 デッドロック回避

確かな情報が予めわかっていれば、リソースの要求を慎重に分析して適切に割当を行うことでデッドロックは回避できる。銀行家のアルゴリズムや2相ロックなどがある。

少し飛びます

3.6 RAMディスク

メモリドライバを使うと、メモリの任意の場所にアクセスすることができる。主な用途は、メモリの一部を普通のディスク装置のように利用することであり、ここではRAMディスクドライバとして説明されている。

ブロックデバイスはブロックの読み/書きという2つのコマンドが使われる記憶媒体である。通常ブロックは回転系のメモリ(フロッピーディスク等)に格納されるが、RAMディスクはもっと単純で、あらかじめ割り当てた主メモリの一部にブロックを格納する

 

RAMディスクは割り当てるメモリ容量によっていくつかのブロックに分けられる。各々のブロックサイズは実際のディスク装置を使う場合と同じ。ドライバはブロックを読み書きするためのメッセージを受信した時、要求されたブロックがRAMディスクディスクのメモリ上のどこにあるかを計算し、フロッピーディスクやハードディスクではなく、メモリに対してブロック読み書きを実行する。

少し飛びます


3.7 ディスク

3.7.1 ディスクのハードウェア

ディスクの構造説明を読んで図にしてみたけど..
こんな感じ...

(追:シリンダって書いてる部分は「ディスク」です。ディスクが重なったものがシリンダですね)

f:id:unyamahiro:20180501113713p:plain

通常フロッピーディスクでは1周あたり8-32個のセクタを、ハードディスクでは数百以上のセクタを持つ。最も単純な設計では各トラックは同じ数のセクタを持っている。全てのセクタは同じバイト数であるが、当然外周のセクタはディスク中心付近のセクタよりも物理的に長い。データ密度は一番中心のシリンダが明らかに高く、あるディスク装置の設計では内側のトラックを読み書きする時にヘッドの電流を変化させるものがある。これはディスクコントローラのハードウエアで処理されていてユーザに見えることはない。


3.7.2 RAID

Reduntant Array of Independent Disksの頭文字を取ったもの
ディスクアクセスの性能とデータ保護の目的で使われる。

RAID0:ストライピング書きこむデータを分割し、複数のドライブで読み書きを行うことによって高速化

RAID1:ミラーリング同じデータを複数のドライブに書きこむことで冗長性を向上させ可用性を高める。


(
この本にはその他のレベル(RAID2以上)は書いてませんでした)


3.7.3 ディスクのソフトウェア

ディスクブロックを読み書きするのにかかる時間は、シーク(アームが該当シリンダに移動する)、回転待ち(該当セクタをヘッドの下に持ってくる)、データ転送の3つによって決定される。ほとんどのディスクでは、中でもシーク時間が他の2つに比べて大きな割合を占めているので、これを減らすことでシステム効率を改善できる。

・ディスクアームのスケジューリングアルゴリズム
先着順/最短シーク優先/エレベータアルゴリズム


・エラー処理
・トラック単位のキャッシュ機能

 

とりあえず今回ここまで。