ZigでOSを作るシリーズ
| Part1 基本 | Part2 ブート | Part3 割り込み | Part4 メモリ | Part5 プロセス | Part6 FS |
|---|---|---|---|---|---|
| ✅ Done | ✅ Done | ✅ Done | ✅ Done | ✅ Done | 👈 Now |
はじめに
前回はプロセス管理を実装した。
最終回の今回はファイルシステム!VFS(Virtual File System)レイヤーとRAMディスク上の簡単なFSで、ファイルの読み書きを実現する。
ここまでくると、本当にOSらしくなってくるよね。最後の仕上げ、頑張ろう!
ファイルシステムの全体像
┌─────────────────────────────────────────────────────────────────┐
│ ファイルシステム階層構造 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ User Applications / Shell │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ open, read, write, close │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ System Calls │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ VFS (Virtual File System) │ │
│ │ Unified interface for all filesystems │ │
│ └─────────┬─────────────────┬─────────────────┬─────────────┘ │
│ │ │ │ │
│ ↓ ↓ ↓ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ RamFS │ │ Ext2 │ │ FAT32 │ │
│ │ │ │ (future) │ │ (future) │ │
│ └─────┬─────┘ └───────────┘ └───────────┘ │
│ │ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ RAM / Block Device │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
VFSインターフェース
// vfs.zig
const std = @import("std");
const heap = @import("heap.zig");
pub const FileType = enum {
regular,
directory,
symlink,
device,
};
pub const OpenFlags = struct {
read: bool = false,
write: bool = false,
create: bool = false,
truncate: bool = false,
append: bool = false,
pub const READ = OpenFlags{ .read = true };
pub const WRITE = OpenFlags{ .write = true };
pub const RDWR = OpenFlags{ .read = true, .write = true };
pub const CREATE = OpenFlags{ .read = true, .write = true, .create = true };
};
pub const SeekFrom = enum {
start,
current,
end,
};
pub const DirEntry = struct {
name: [256]u8,
name_len: usize,
file_type: FileType,
inode: u64,
size: u64,
};
// inodeメタデータ
pub const Inode = struct {
id: u64,
file_type: FileType,
size: u64,
permissions: u16,
uid: u32,
gid: u32,
created_time: u64,
modified_time: u64,
accessed_time: u64,
link_count: u32,
// FSドライバへの参照
fs: *FileSystem,
fs_data: ?*anyopaque,
};
// ファイル操作インターフェース
pub const FileOperations = struct {
read: ?*const fn (*File, []u8) anyerror!usize = null,
write: ?*const fn (*File, []const u8) anyerror!usize = null,
seek: ?*const fn (*File, i64, SeekFrom) anyerror!u64 = null,
close: ?*const fn (*File) void = null,
};
// ディレクトリ操作インターフェース
pub const DirOperations = struct {
lookup: ?*const fn (*Inode, []const u8) anyerror!?*Inode = null,
readdir: ?*const fn (*Inode, usize) anyerror!?DirEntry = null,
create: ?*const fn (*Inode, []const u8, FileType) anyerror!*Inode = null,
remove: ?*const fn (*Inode, []const u8) anyerror!void = null,
mkdir: ?*const fn (*Inode, []const u8) anyerror!*Inode = null,
};
// オープンしたファイル
pub const File = struct {
inode: *Inode,
position: u64,
flags: OpenFlags,
ops: *const FileOperations,
pub fn read(self: *File, buf: []u8) !usize {
if (self.ops.read) |read_fn| {
return read_fn(self, buf);
}
return error.NotSupported;
}
pub fn write(self: *File, data: []const u8) !usize {
if (self.ops.write) |write_fn| {
return write_fn(self, data);
}
return error.NotSupported;
}
pub fn seek(self: *File, offset: i64, whence: SeekFrom) !u64 {
if (self.ops.seek) |seek_fn| {
return seek_fn(self, offset, whence);
}
return error.NotSupported;
}
pub fn close(self: *File) void {
if (self.ops.close) |close_fn| {
close_fn(self);
}
}
};
// ファイルシステムインターフェース
pub const FileSystem = struct {
name: []const u8,
root: ?*Inode,
file_ops: FileOperations,
dir_ops: DirOperations,
mount: ?*const fn (*FileSystem, []const u8) anyerror!void,
unmount: ?*const fn (*FileSystem) void,
};
// マウントポイント
const MountPoint = struct {
path: [256]u8,
path_len: usize,
fs: *FileSystem,
};
var mount_points: [16]?MountPoint = [_]?MountPoint{null} ** 16;
var root_fs: ?*FileSystem = null;
pub fn init() void {
// ルートFSはramfsで初期化
}
pub fn mount(fs: *FileSystem, path: []const u8) !void {
// パスが "/"の場合はルート
if (std.mem.eql(u8, path, "/")) {
root_fs = fs;
if (fs.mount) |mount_fn| {
try mount_fn(fs, path);
}
return;
}
// マウントポイントを追加
for (&mount_points) |*mp| {
if (mp.* == null) {
var mount_path: [256]u8 = [_]u8{0} ** 256;
@memcpy(mount_path[0..path.len], path);
mp.* = .{
.path = mount_path,
.path_len = path.len,
.fs = fs,
};
if (fs.mount) |mount_fn| {
try mount_fn(fs, path);
}
return;
}
}
return error.TooManyMounts;
}
pub fn open(path: []const u8, flags: OpenFlags) !*File {
const inode = try resolvePath(path);
const file = @as(*File, @ptrCast(@alignCast(heap.alloc(@sizeOf(File)) orelse return error.OutOfMemory)));
file.* = .{
.inode = inode,
.position = 0,
.flags = flags,
.ops = &inode.fs.file_ops,
};
return file;
}
fn resolvePath(path: []const u8) !*Inode {
// ルートから始まる
const fs = root_fs orelse return error.NoRootFs;
var current = fs.root orelse return error.NoRootInode;
// パスを分割して辿る
var iter = std.mem.splitScalar(u8, path, '/');
while (iter.next()) |component| {
if (component.len == 0) continue;
if (fs.dir_ops.lookup) |lookup| {
current = try lookup(current, component) orelse return error.FileNotFound;
} else {
return error.NotSupported;
}
}
return current;
}
pub fn readdir(path: []const u8) !*Inode {
return try resolvePath(path);
}
RAMファイルシステム
メモリ上で動作するシンプルなFS。
// ramfs.zig
const std = @import("std");
const vfs = @import("vfs.zig");
const heap = @import("heap.zig");
const MAX_FILES: usize = 256;
const MAX_FILE_SIZE: usize = 64 * 1024; // 64KB
// RAMFSのinodeデータ
const RamFsInode = struct {
name: [256]u8,
name_len: usize,
data: ?[]u8,
children: [64]?*vfs.Inode,
children_count: usize,
parent: ?*vfs.Inode,
};
var inodes: [MAX_FILES]?*vfs.Inode = [_]?*vfs.Inode{null} ** MAX_FILES;
var inode_data: [MAX_FILES]?RamFsInode = [_]?RamFsInode{null} ** MAX_FILES;
var next_inode_id: u64 = 1;
pub var ramfs: vfs.FileSystem = .{
.name = "ramfs",
.root = null,
.file_ops = .{
.read = ramfsRead,
.write = ramfsWrite,
.seek = ramfsSeek,
.close = ramfsClose,
},
.dir_ops = .{
.lookup = ramfsLookup,
.readdir = ramfsReaddir,
.create = ramfsCreate,
.remove = ramfsRemove,
.mkdir = ramfsMkdir,
},
.mount = ramfsMount,
.unmount = ramfsUnmount,
};
fn ramfsMount(fs: *vfs.FileSystem, _: []const u8) anyerror!void {
// ルートディレクトリを作成
const root = createInode("/", .directory) orelse return error.OutOfMemory;
fs.root = root;
}
fn ramfsUnmount(_: *vfs.FileSystem) void {
// クリーンアップ
}
fn createInode(name: []const u8, file_type: vfs.FileType) ?*vfs.Inode {
// 空きスロットを探す
for (&inodes, 0..) |*slot, i| {
if (slot.* == null) {
const inode_ptr = @as(*vfs.Inode, @ptrCast(@alignCast(heap.alloc(@sizeOf(vfs.Inode)) orelse return null)));
var name_buf: [256]u8 = [_]u8{0} ** 256;
@memcpy(name_buf[0..name.len], name);
inode_data[i] = .{
.name = name_buf,
.name_len = name.len,
.data = null,
.children = [_]?*vfs.Inode{null} ** 64,
.children_count = 0,
.parent = null,
};
inode_ptr.* = .{
.id = next_inode_id,
.file_type = file_type,
.size = 0,
.permissions = 0o755,
.uid = 0,
.gid = 0,
.created_time = 0,
.modified_time = 0,
.accessed_time = 0,
.link_count = 1,
.fs = &ramfs,
.fs_data = &inode_data[i].?,
};
next_inode_id += 1;
slot.* = inode_ptr;
return inode_ptr;
}
}
return null;
}
fn getInodeData(inode: *vfs.Inode) ?*RamFsInode {
if (inode.fs_data) |data| {
return @ptrCast(@alignCast(data));
}
return null;
}
fn ramfsLookup(parent: *vfs.Inode, name: []const u8) anyerror!?*vfs.Inode {
const data = getInodeData(parent) orelse return null;
for (data.children[0..data.children_count]) |child_opt| {
if (child_opt) |child| {
const child_data = getInodeData(child) orelse continue;
if (std.mem.eql(u8, child_data.name[0..child_data.name_len], name)) {
return child;
}
}
}
return null;
}
fn ramfsReaddir(dir: *vfs.Inode, index: usize) anyerror!?vfs.DirEntry {
const data = getInodeData(dir) orelse return null;
if (index >= data.children_count) return null;
if (data.children[index]) |child| {
const child_data = getInodeData(child) orelse return null;
var entry: vfs.DirEntry = undefined;
@memcpy(entry.name[0..child_data.name_len], child_data.name[0..child_data.name_len]);
entry.name_len = child_data.name_len;
entry.file_type = child.file_type;
entry.inode = child.id;
entry.size = child.size;
return entry;
}
return null;
}
fn ramfsCreate(parent: *vfs.Inode, name: []const u8, file_type: vfs.FileType) anyerror!*vfs.Inode {
const parent_data = getInodeData(parent) orelse return error.InvalidInode;
if (parent_data.children_count >= 64) {
return error.DirectoryFull;
}
const new_inode = createInode(name, file_type) orelse return error.OutOfMemory;
const new_data = getInodeData(new_inode) orelse return error.InvalidInode;
new_data.parent = parent;
parent_data.children[parent_data.children_count] = new_inode;
parent_data.children_count += 1;
return new_inode;
}
fn ramfsRemove(parent: *vfs.Inode, name: []const u8) anyerror!void {
const parent_data = getInodeData(parent) orelse return;
for (parent_data.children[0..parent_data.children_count], 0..) |child_opt, i| {
if (child_opt) |child| {
const child_data = getInodeData(child) orelse continue;
if (std.mem.eql(u8, child_data.name[0..child_data.name_len], name)) {
// ファイルデータを解放
if (child_data.data) |data| {
heap.free(data.ptr);
}
// 配列から削除
for (i..parent_data.children_count - 1) |j| {
parent_data.children[j] = parent_data.children[j + 1];
}
parent_data.children_count -= 1;
return;
}
}
}
}
fn ramfsMkdir(parent: *vfs.Inode, name: []const u8) anyerror!*vfs.Inode {
return ramfsCreate(parent, name, .directory);
}
fn ramfsRead(file: *vfs.File, buf: []u8) anyerror!usize {
const data = getInodeData(file.inode) orelse return 0;
const file_data = data.data orelse return 0;
const start = @min(file.position, file_data.len);
const end = @min(start + buf.len, file_data.len);
const bytes_read = end - start;
@memcpy(buf[0..bytes_read], file_data[start..end]);
file.position += bytes_read;
return bytes_read;
}
fn ramfsWrite(file: *vfs.File, data_to_write: []const u8) anyerror!usize {
const inode_data_ptr = getInodeData(file.inode) orelse return error.InvalidInode;
// 必要に応じてバッファを確保/拡張
const required_size = file.position + data_to_write.len;
if (inode_data_ptr.data == null) {
const new_buf = heap.alloc(required_size) orelse return error.OutOfMemory;
inode_data_ptr.data = new_buf[0..required_size];
} else if (required_size > inode_data_ptr.data.?.len) {
const new_buf = heap.alloc(required_size) orelse return error.OutOfMemory;
@memcpy(new_buf[0..inode_data_ptr.data.?.len], inode_data_ptr.data.?);
heap.free(inode_data_ptr.data.?.ptr);
inode_data_ptr.data = new_buf[0..required_size];
}
@memcpy(inode_data_ptr.data.?[file.position..][0..data_to_write.len], data_to_write);
file.position += data_to_write.len;
file.inode.size = @max(file.inode.size, file.position);
return data_to_write.len;
}
fn ramfsSeek(file: *vfs.File, offset: i64, whence: vfs.SeekFrom) anyerror!u64 {
const new_pos: i64 = switch (whence) {
.start => offset,
.current => @as(i64, @intCast(file.position)) + offset,
.end => @as(i64, @intCast(file.inode.size)) + offset,
};
if (new_pos < 0) return error.InvalidSeek;
file.position = @intCast(new_pos);
return file.position;
}
fn ramfsClose(_: *vfs.File) void {
// 特に何もしない
}
// 初期ファイルを作成
pub fn createInitialFiles() void {
const root = ramfs.root orelse return;
// /etc ディレクトリ
if (ramfs.dir_ops.mkdir) |mkdir| {
if (mkdir(root, "etc")) |etc| {
// /etc/hostname
if (ramfs.dir_ops.create) |create| {
if (create(etc, "hostname", .regular)) |hostname| {
const data = getInodeData(hostname) orelse return;
const content = "zigOS\n";
const buf = heap.alloc(content.len) orelse return;
@memcpy(buf[0..content.len], content);
data.data = buf[0..content.len];
hostname.size = content.len;
} else |_| {}
}
// /etc/motd
if (ramfs.dir_ops.create) |create| {
if (create(etc, "motd", .regular)) |motd| {
const data = getInodeData(motd) orelse return;
const content =
\\Welcome to Zig OS!
\\
\\Type 'help' for available commands.
\\
;
const buf = heap.alloc(content.len) orelse return;
@memcpy(buf[0..content.len], content);
data.data = buf[0..content.len];
motd.size = content.len;
} else |_| {}
}
} else |_| {}
}
// /tmp ディレクトリ
if (ramfs.dir_ops.mkdir) |mkdir| {
_ = mkdir(root, "tmp") catch {};
}
// /home ディレクトリ
if (ramfs.dir_ops.mkdir) |mkdir| {
_ = mkdir(root, "home") catch {};
}
}
シェルコマンドの追加
// shell.zig - ファイルシステムコマンド
const vfs = @import("vfs.zig");
const ramfs = @import("ramfs.zig");
const vga = @import("vga.zig");
fn processCommand(cmd: []const u8) void {
var args = splitCommand(cmd);
if (strEql(args[0], "ls")) {
cmdLs(args[1..]);
} else if (strEql(args[0], "cat")) {
cmdCat(args[1..]);
} else if (strEql(args[0], "echo")) {
cmdEcho(args[1..]);
} else if (strEql(args[0], "mkdir")) {
cmdMkdir(args[1..]);
} else if (strEql(args[0], "touch")) {
cmdTouch(args[1..]);
} else if (strEql(args[0], "rm")) {
cmdRm(args[1..]);
} else if (strEql(args[0], "write")) {
cmdWrite(args[1..]);
}
// ... 他のコマンド
}
fn cmdLs(args: [][]const u8) void {
const path = if (args.len > 0) args[0] else "/";
const dir = vfs.readdir(path) catch {
vga.print("ls: cannot access '");
vga.print(path);
vga.print("'\n");
return;
};
const data = @as(*ramfs.RamFsInode, @ptrCast(@alignCast(dir.fs_data orelse return)));
vga.print("\nContents of ");
vga.print(path);
vga.print(":\n");
var idx: usize = 0;
while (idx < data.children_count) : (idx += 1) {
if (ramfs.ramfs.dir_ops.readdir) |readdir| {
if (readdir(dir, idx)) |entry| {
// ディレクトリは色を変える
if (entry.file_type == .directory) {
vga.setColor(.Cyan, .Black);
printName(entry.name[0..entry.name_len]);
vga.print("/");
vga.setColor(.White, .Black);
} else {
printName(entry.name[0..entry.name_len]);
}
vga.print(" ");
printNumber(entry.size);
vga.print(" bytes\n");
} else |_| {}
}
}
vga.print("\n");
}
fn cmdCat(args: [][]const u8) void {
if (args.len == 0) {
vga.print("cat: missing file operand\n");
return;
}
const file = vfs.open(args[0], vfs.OpenFlags.READ) catch {
vga.print("cat: cannot open '");
vga.print(args[0]);
vga.print("'\n");
return;
};
defer file.close();
var buf: [512]u8 = undefined;
while (true) {
const bytes = file.read(&buf) catch break;
if (bytes == 0) break;
for (buf[0..bytes]) |c| {
vga.putChar(c);
}
}
}
fn cmdEcho(args: [][]const u8) void {
for (args) |arg| {
vga.print(arg);
vga.print(" ");
}
vga.print("\n");
}
fn cmdMkdir(args: [][]const u8) void {
if (args.len == 0) {
vga.print("mkdir: missing operand\n");
return;
}
// 簡略化:ルートに作成
const root = ramfs.ramfs.root orelse return;
if (ramfs.ramfs.dir_ops.mkdir) |mkdir| {
_ = mkdir(root, args[0]) catch {
vga.print("mkdir: cannot create directory\n");
return;
};
vga.print("Directory created: ");
vga.print(args[0]);
vga.print("\n");
}
}
fn cmdTouch(args: [][]const u8) void {
if (args.len == 0) {
vga.print("touch: missing operand\n");
return;
}
const root = ramfs.ramfs.root orelse return;
if (ramfs.ramfs.dir_ops.create) |create| {
_ = create(root, args[0], .regular) catch {
vga.print("touch: cannot create file\n");
return;
};
vga.print("File created: ");
vga.print(args[0]);
vga.print("\n");
}
}
fn cmdRm(args: [][]const u8) void {
if (args.len == 0) {
vga.print("rm: missing operand\n");
return;
}
const root = ramfs.ramfs.root orelse return;
if (ramfs.ramfs.dir_ops.remove) |remove| {
remove(root, args[0]) catch {
vga.print("rm: cannot remove\n");
return;
};
vga.print("Removed: ");
vga.print(args[0]);
vga.print("\n");
}
}
fn cmdWrite(args: [][]const u8) void {
if (args.len < 2) {
vga.print("write: usage: write <file> <content>\n");
return;
}
const file = vfs.open(args[0], vfs.OpenFlags.CREATE) catch {
vga.print("write: cannot open file\n");
return;
};
defer file.close();
// 残りの引数を結合して書き込み
for (args[1..]) |arg| {
_ = file.write(arg) catch {};
_ = file.write(" ") catch {};
}
_ = file.write("\n") catch {};
vga.print("Written to: ");
vga.print(args[0]);
vga.print("\n");
}
fn splitCommand(cmd: []const u8) [16][]const u8 {
var result: [16][]const u8 = [_][]const u8{""} ** 16;
var count: usize = 0;
var start: usize = 0;
var in_word = false;
for (cmd, 0..) |c, i| {
if (c == ' ' or c == '\t') {
if (in_word) {
result[count] = cmd[start..i];
count += 1;
in_word = false;
}
} else {
if (!in_word) {
start = i;
in_word = true;
}
}
}
if (in_word and count < 16) {
result[count] = cmd[start..];
}
return result;
}
fn printName(name: []const u8) void {
for (name) |c| {
if (c == 0) break;
vga.putChar(c);
}
}
fn printNumber(n: u64) void {
// 実装省略
}
fn strEql(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
for (a, b) |ca, cb| {
if (ca != cb) return false;
}
return true;
}
メインの更新
// boot.zig
const vfs = @import("vfs.zig");
const ramfs = @import("ramfs.zig");
export fn kernel_main() callconv(.C) noreturn {
// ... 前回までの初期化 ...
// ファイルシステム初期化
vga.print("[OK] VFS initialized\n");
vfs.init();
// RAMFSをマウント
vga.print("[OK] RamFS mounted at /\n");
vfs.mount(&ramfs.ramfs, "/") catch {};
// 初期ファイルを作成
ramfs.createInitialFiles();
vga.print("[OK] Initial files created\n\n");
// MOTDを表示
if (vfs.open("/etc/motd", vfs.OpenFlags.READ)) |file| {
var buf: [512]u8 = undefined;
if (file.read(&buf)) |bytes| {
for (buf[0..bytes]) |c| {
vga.putChar(c);
}
} else |_| {}
file.close();
} else |_| {}
// シェル起動
shell.run();
while (true) {
asm volatile ("hlt");
}
}
実行結果
Zig OS Kernel booting...
[OK] GDT initialized
[OK] IDT initialized
[OK] PIC initialized
[OK] PIT initialized (1000Hz)
[OK] Keyboard initialized
[OK] Memory manager initialized
[OK] Scheduler initialized
[OK] VFS initialized
[OK] RamFS mounted at /
[OK] Initial files created
Welcome to Zig OS!
Type 'help' for available commands.
zigOS> ls /
Contents of /:
etc/ 0 bytes
tmp/ 0 bytes
home/ 0 bytes
zigOS> ls /etc
Contents of /etc:
hostname 6 bytes
motd 51 bytes
zigOS> cat /etc/hostname
zigOS
zigOS> touch myfile.txt
File created: myfile.txt
zigOS> write myfile.txt Hello World from Zig OS!
Written to: myfile.txt
zigOS> cat myfile.txt
Hello World from Zig OS!
zigOS> mkdir test
Directory created: test
zigOS> ls /
Contents of /:
etc/ 0 bytes
tmp/ 0 bytes
home/ 0 bytes
myfile.txt 26 bytes
test/ 0 bytes
zigOS>
まとめ
今回実装したこと:
| コンポーネント | 説明 |
|---|---|
| VFS | 統一されたファイルシステムインターフェース |
| Inode | ファイル/ディレクトリのメタデータ |
| RamFS | メモリ上のファイルシステム |
| シェルコマンド | ls, cat, touch, mkdir, rm, write |
シリーズ全体のまとめ
6回にわたって、Zigで小さなOSを作ってきた。
┌─────────────────────────────────────────────────────────────────┐
│ Zig OS アーキテクチャ図 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Shell │ │
│ └─────────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────┴─────────────────────────────────┐ │
│ │ File System │ │
│ │ ┌─────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ VFS │ │ RamFS │ │ │
│ │ └─────────────────┘ └────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Process Management │ │
│ │ ┌─────────┐ ┌────────────┐ ┌───────────────────────┐ │ │
│ │ │ Task │ │ Scheduler │ │ Context Switch │ │ │
│ │ └─────────┘ └────────────┘ └───────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Memory Management │ │
│ │ ┌─────────┐ ┌────────────┐ ┌───────────────────────┐ │ │
│ │ │ PMM │ │ VMM │ │ Heap Allocator │ │ │
│ │ └─────────┘ └────────────┘ └───────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Interrupt Handling │ │
│ │ ┌─────────┐ ┌────────────┐ ┌───────────────────────┐ │ │
│ │ │ IDT │ │ PIC │ │ Exception Handlers │ │ │
│ │ └─────────┘ └────────────┘ └───────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Device Drivers │ │
│ │ ┌─────────┐ ┌────────────┐ ┌───────────────────────┐ │ │
│ │ │ VGA │ │ Keyboard │ │ PIT Timer │ │ │
│ │ └─────────┘ └────────────┘ └───────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Boot / Hardware │ │
│ │ ┌─────────┐ ┌────────────┐ ┌───────────────────────┐ │ │
│ │ │ GDT │ │ Paging │ │ Multiboot2 │ │ │
│ │ └─────────┘ └────────────┘ └───────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| Part | 内容 |
|---|---|
| Part1 | Zig基本、comptime、no_std、VGA |
| Part2 | Multiboot2、GDT、ページング、64bit |
| Part3 | IDT、PIC、タイマー、キーボード |
| Part4 | 物理メモリ、仮想メモリ、ヒープ |
| Part5 | タスク、コンテキストスイッチ、スケジューラ |
| Part6 | VFS、RamFS、ファイル操作 |
Zigの良さがわかったかな?
-
comptimeでコンパイル時計算 - 明示的なアロケータ管理
- インラインアセンブリの柔軟性
- Cとの相互運用性
- 安全性とパフォーマンスの両立
この記事シリーズが、Zigでのシステムプログラミングに興味を持つきっかけになれば嬉しい!
シリーズ全体が役に立ったら、いいね・ストックしてもらえると嬉しいです!