RubyでOSやGCを書く野望
現在、mmcというmrubyをC(に限らないのですが)に変換するプログラムを書いています。抽象実行とエスケープ解析で、GCのサポートや、selfが常に渡されることなどを除いて、手書きのCに近いコードが出てきます。これを使ってOSが書けないかと思い、色々試しています。
もちろん、普通のRubyでは書けないですけどいろいろ拡張して出来そうな気がしてきました。その拡張の詳細を忘れに用にメモしておきます。
拡張のためのモジュール
OSを書くための拡張はMMC, HALと2つのモジュールに全て閉じ込めてあります。名前空間は汚したくないですから。
MMCモジュール
mmcのメタ情報やコード生成の制御をおこなうためのメソッドやクラスが入っています。
MMC::attribute
メソッドどの属性を設定する。gccのattributeやRustのメソッド定義の前にある#[...]みたいなやつ。今の所、sectionしかサポートしていない。生成したメソッドを特別なアドレスを割り当てるために使う。
例
MMC::attribute(:section, "BOOT")
def boot
cpu = HAL::CPU.new(0)
MMC::class_sizeof(クラス) 未実装
そのクラスのインスタンスのサイズを返す。単位はバイト
MMC::instance_sizeof(オブジェクト) 未実装
オブジェクトのサイズを返す。単位はバイト
MMC::offsetof(オブジェクト, インスタンス変数) 未実装
オブジェクト内のインスタンス変数のオフセットを返す。インスタンス変数はシンボル
HALモジュール
ハードウエアに依存したクラスなどを閉じ込めたモジュール
HAL::CPUクラス
CPUを抽象化したクラス。インストラクションセットを生成するメソッドや、CPUに紐付けされたメモリやレジスタのオブジェクトを持つ。CPUクラスのインスタンスをえるにはCPU番号が必要。これはマルチCPU(しかもヘトロなシステム)に対応するため。
CPU#regsメソッド
CPUのレジスタセットを抽象化したRegsクラスを返す。レジスタ1つではなくCPUにあるレジスタ全部であることに注意。
CPU#memメソッド
CPUのアドレス空間を抽象化したMemクラスを返す。
CPU#jmp, CPU#labelメソッド
ジャンプ命令やラベルの定義を生成するメソッド
Regsクラス
CPUのレジスタセットを抽象化したクラス。レジスタではなくレジスタ全体にするのは単なる代入ではフックできないが、[]=をオーバーライドするのは色々カスタマイズできるから。
Regs#[](name) メソッド
nameの名前のレジスタに格納された値を返す、のだが返しても大抵の場合意味が無いのでアセンブラのコード生成のために使う。次に説明する[]=()メソッドと一緒に使う
Regs#[]=(name、val) メソッド
nameの名前のレジスタに値を設定する。とても限定された場合しか使えないが、アセンブラを直接生成する。たとえば、
regs[:eax] = regs[:eax] + regs[:ebx]
と書くと、
add EAX, EBX
と生成される。(実際はgasのAT&Tフォーマットだけどなじみが多いだろうからIntelフォーマットで説明した)
Memクラス
CPUのメモリ(正確には論理アドレス空間)を抽象化したもの。あたかも配列のようにメモリを見せたり、メモリ空間の設定をしたりする。
Mem#[address, size = nil]
addressが整数(またはアドレス用のクラスを用意する予定)ならそのアドレスの内容。sizeが指定されたらそのバイト数。デフォルトはintと同じ大きさ。
addressがRegクラス(Regs#[]メソッドの戻り値)ならアセンブラの命令を生成する。
regs[:eax] = mem[regs[:eax] + 4]
だと、
mov EAX, 4(EAX)
というコードが生成される。
Mem#[]=(address, size = nil, val)
addressとvalが整数ならaddressに示されるメモリを書きこむ。addressやvalがRegクラスだとアセンブラを生成する。
Mem#static_cast(address, klass)
addressに示されるメモリ領域をklassクラスのインスタンスとして返す。返って来たオブジェクトは普通のオブジェクトのように使えるけど(initializeメソッドは呼び出されない)、GCのことは全く考えていないので注意。
Mem#static_allocate(klass)
klassクラスのインスタンスを静的に確保して(Cレベルではグローバル変数を定義して)、そのオブジェクトを返す。返ってきたオブジェクトは普通のオブジェクトの様に使えるけど (以下同文)。
Mem#static_array_allocate(klass, num)
klassクラスのインスタンスnum個を静的に確保して(Cレベルではグローバル変数を定義して)、配列オブジェクトとして返す。返ってきたオブジェクトは普通のオブジェクトの様に使えるけど (以下同文)
MMCではエスケープしない配列はCレベルの配列と同じフォーマットです。
プログラム例
例えばK&Rにあるような簡単なメモリアロケータを考えてみましょう。こんな感じになるかなと思います。(まだ動かないです)
class Header
def initiaize
@size = 0
@next = 0 # NULL pointer
end
attr_accessor :size
attr_accessor :next
end
class Allocator
def initialize
cpu = HAL::CPU.new(0) # 0はCPU番号
mem = cpu.mem
arena = mem.static_array_allocate(Fixnum, 65536) # アロケートのためのメモリ領域
header = mem::static_cast(arena, Header)
header.size = 65536
header.next = 0
@root = header
end
def malloc(klass) #このmallocはサイズではなくクラスのインスタンスを返す
cpu = HAL::CPU.new(0) # 0はCPU番号
mem = cpu.mem
size = MMC::sizeof(klass)
cchunk = @root
pchunk = nil
while cchunk and cchunk.size < size then
pchunk = cchunk
cchunk = cchunk.next
end
result = nil
if cchunk then
result = mem::static_cast(cchunk, klass) #チャンクの先頭を切りだす
nchunk = mem::statioc_cast(cchunk + size, Header) #新しいヘッダを作る。前のチャンクは
nchunk.size = cchunk.size - size #ヘッダのサイズを切りだし
nchunk.next = cchunk.next # 次のチャンクは変らない
if pchunk then #番兵を用意した方がいいかも
pchunk.next = nchunk
else
@root = nchunk
end
end
result
end
end
Author And Source
この問題について(RubyでOSやGCを書く野望), 我々は、より多くの情報をここで見つけました https://qiita.com/miura1729/items/2e355466b6c823e1a413著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .