読者です 読者をやめる 読者になる 読者になる

ファイルシステム実装中

自作OS

だらだろext2の実装を始めたので、忘れないようにc⌒っ゜д゜)っφ メモメモ...
基本的にはLinuxの実装方法を参考にしつつ進めてます。ソースはこちら
とりあえず、極々基本的なreadの流れが出来たので、めもっとかないと自分で忘れてしまいますorz
なんとなくvfsもどきな構造になってます。。。

最初に、init/cmain.cのcmain()ではファイルシステムの初期化と、ルートファイルシステムのマウントの関数を呼び出し。

	// initialize file system.
	ext2_fs_init();

	// mount root file system.
	mount_root_fs();

ext2_fs_init()は1行の関数で、register_file_system()を呼ぶだけ。

void ext2_fs_init(void)
{
	register_file_system(&ext2_fs_type);
}

ext2_fs_typeは構造体で、スーパーブロックを読み込むような関数なんかを定義。

static struct file_system_type ext2_fs_type = {
	.name = "ext2",
	.get_sb = &ext2_get_sb,
};

定義はこんな感じです。

struct file_system_type {
	const char *name;
	int (*get_sb)(struct vfs_mount *vmount);
	struct file_system_type *next;
};

見れば分かるとおり、連結リストになっているので、register_file_system()は渡されたデータを連結リストにつなぐだけです。
マウントの処理は、ドライバをある程度決め打ちにしてますが、流れてきには、ATAディスクのドライバがカーネルにあれば、
そのディスクをオープンしてアクセス可能な状態にしてます。
このドライバは前に作ったやつです。日記に書いた時から多少は変わってますが。CHS方式は消して、LBAのみにしたりと。
そんで、ドライバとマウントポイントを関連付けて、さらにマウントポイントとファイルシステムのタイプをセットしてます。

int mount_root_fs(void)
{
	driver = get_blk_driver("ATA disk");
	if (!driver) {
		printk("There is no ATA disk driver!\n");
		return -1;
	}

	if (driver->op->open()) {
		printk("Couldn't open disk\n");
		return -1;
	}

	set_mount_point("/", driver);
	
	read_super_block("ext2", "/");

	printk("rootfs mount finished\n");
	return 0;
}

set_mount_point()で行うのは、以下の構造体にデータをセットして、連結リストにつなぐだけです。

struct vfs_mount {
	const char *m_point;
	const struct blk_device_drivers *driver;
	struct vfs_mount *next;
};

そして、read_super_block()でスーパーブロックの読み出しに入ります。これはext2のコードではなくて、vfsもどきのコードです。
最初に、find_file_system_type()で引数の1番目に指定したファイルシステムが登録されているか調べてます。
次に、マウントポイントに応じたstruct vfs_mountを取得します。
そうすると、その構造体にはスーパーブロックを読み出すための関数がセットされているので、p->get_sb(point);と言うかたちで関数を呼び出せます。

int read_super_block(const char *fs_name, const char *mount_point)
{
	struct file_system_type *p;
	struct vfs_mount *point;

	p = find_file_system_type(fs_name);
	if (!p) {
		printk("%s file system hasn't been registered yet\n", fs_name);
		return -1;
	}

	point = get_mount_point(mount_point);
	if (!point) {
		printk("there is no mount point for [%s]\n", mount_point);
		return -1;
	}

	printk("mount point is [%s]\n", point->m_point);

	return p->get_sb(point);
}

そして実際のスーパーブロックの読み出しですが、まだ出来てません/(^o^)\
今のところ、ディスクから1ブロックだけ読み込むような感じです。

static int ext2_get_sb(struct vfs_mount *vmount)
{
	block_data_t sblock;
	int ret;

	printk("%s\n", __FUNCTION__);

	ret = read_one_block(vmount->driver, &sblock);

	{
		int i;
		for (i = 0; i < 32; i++)
			printk("0x%x ", sblock.data[i]);
	}
	return 0;
}

read_one_block()はマクロです。

#define read_one_block(driver, out) read_blocks((driver), (out), 1)

read_blocks()はブロックデバイス共通のコードにいて、ATAのディスクドライバを直接呼ぶ形にはしてません。
単に、blk_device_drivers構造体にセットされてるread()を呼ぶだけです。
さらに、こいつもまだちゃんと作ってなくて、ATAのドライバを書いたときに使ったテストデータを読み込むだけです。
このときのテストデータは10MBのHDDイメージで、minixfsでフォーマットして、こんなテキストファイルが1つある状態です。

echo "ABCDE" > /media/test/test.txt

実装は全然入ってません、、、
インターフェースも足りてなくて、そもそも、どの論理セクタを読むのか指定されてないですからね。

int read_blocks(const struct blk_device_drivers *blk_driver, block_data_t *out, int count)
{
	int i;
	int sector = 0;

	for (i = 0; i < count; i++) {
		blk_driver->op->read(0, 222, out->sector, 256);
	}

	return 0;
}

まぁ、なにはともあれ、blk_device_driversに登録されてるread()を読んで、データを取得します。
この構造体はこういう形です。

struct blk_dev_driver_operations {
	char name[32]; // driver name.
	int (*open)(void);
	int (*close)(void);
	int (*read)(int device, u_int32_t sector, 
		    sector_t *buf, size_t buf_size);
	int (*write)(void); // temporary definition.
	int (*ioctl)(void); // temporary definition.
	int (*scattered_io)(void); // temporary definition.
};

ATAのドライバのコードでは、こういう初期化をしてます。

struct blk_dev_driver_operations ata_dev = {
	.name = "ATA disk",
	.open = &open_ATA_disk,
	.close = &close_ATA_disk,
	.read = &read_sector,
	.write = &write_sector,
};

read()を呼ぶことで、ドライバがディスクからデータを読み込んで、引数のoutにデータを入れてあげる感じです。
read_sector()はこれの中にあります。

動いている画面はこれ。

そんなわけで、vfsもどき、ext2、ブロックデバイスドライバ共通の処理、ATAのドライバを連携させる流れは一応できました。