ゼロからのOS自作入門 読みメモ 1章
PCの仕組みとハローワールド
- バイナリエディタで本のバイナリを写経してみる
$ sum BOOTX64.EFI 12430 2 BOOTX64.EFI
本の値と一致した!
- BOOTX64.EFI を含むディスクイメージを作成する
$ qemu-img create -f raw disk.img 200M
-f
オプションでディスクイメージの形式を指定
$ mkfs.fat -n 'HOGE OS' -s 2 -f 2 -R 32 -F 32 disk.img
mkfs.fat
はフォーマットをおこなうコマンド-n
でボリューム名をつける-s
ではクラスタごとのセクタの数をきめる-f
FATの数を決める デフォルトは2(FATの数ってなんだろう)-R
reserved sectors の数を決める-F
FATのタイプ指定(12, 16, 32ビットから指定する)- 見た Design of the FAT file system - Wikipedia
- Mac だと
newfs_msdos
で使えるっぽい?
newfs_msdos -v 'HOGE OS' -c 2 -n 2 -r 32 -F 32 disk.img
- オプションは多分これ(
-c
==-s
,-n
==-f
,-r
==-R
) newfs_msdos
の前にMacだとディスクイメージにattachしないといけなさそう?
$ hdiutil attach -nomount ./disk.img > dev/disk2 # <=これを指定する $ newfs_msdos -v 'HOGE OS' -c 2 -n 2 -r 32 -F 32 disk2 $ hdiutil mountvol /dev/disk2 # これでマウントされる > /dev/disk2 /Volumes/HOGE OS $ sudo mkdir /Volumes/HOGE\ OS/EFI/BOOT $ sudo cp BOOTX64.EFI /Volumes/HOGE\ OS/EFI/BOOT/BOOTX64.EFI $ hdiutil detach disk2
- ovmf を取ってきてbuild GitHub - tianocore/edk2: EDK II
$ cp $HOME/edk2/Build/OvmfX64/DEBUG_XCODE5/FV/OVMF.fd . $ qemu-system-x86_64 -bios OVMF.fd -hda disk.img
動いた!
C言語でHello Worldは昔やったので次は2章読もう
メタプログラミングRuby読みメモ 6
9章 Active Record の設計
ActiveRecord::Base
- Active Record は、Active Support と Active Modelの2つのライブラリに大きく依存している
- ActiveSupport::Autoloadモジュールのautoloadを用いて多くのモジュールのincludeを実現している
- autoload https://docs.ruby-lang.org/ja/latest/method/Module/i/autoload.html
モジュール名を最初に参照したときに自動的にソースを探してrequireする
ActiveRecors::Base はモジュールの外側にある機能をまとめている
- オートローディングのおかげでソースコードを先にrequireしなくてもincludeできる
- ActiveRecordはデータベースの操作とオブジェクトモデルの操作の二つに分割された
- データベースの操作はActiveRecordに残り、オブジェクトモデルの操作はActiveModelに移動した
10章 Active Support の Concern モジュール
- Rails のモジュールは、モジュールをインクルードするとインスタンスメソッドとクラスメソッドの両方が手に入る
Concern 以前の Rails
- ActiveRecord::Validation で定義されていた
- ActiveRecord::Base が Validations をインクルードすると
- Validations のインスタンスメソッドが Base クラスのインスタンスメソッドになる
- ValidationsのincludedというフックメソッドをActiveRecord::Baseを引数にして呼び出す
- フックメソッドではActiveRecord::BaseクラスをActiveRecord::Validations::ClassMethodモジュールでextendしている。
- これはクラス拡張なので、ClassMethods のメソッドは Base のクラスメソッドになる
- その結果、Base はインスタンスメソッドとクラスメソッドの両方を手に入れることができた
11章 alias_method_chain の盛衰
alias_method_chain ターゲットのメソッドの名前, 追加機能の名前
とすると- メソッドwith追加機能
- メソッドwithout追加機能
- という二つのメソッドを作り出す
- これは元のメソッドを新しい追加機能で包んだメソッドを作り出してくれる
- 古いバージョンのActiveRecord::Validationsはsaveとsave!をハックしてvalidationを追加している
- validationしたくないsaveをするのであればsave_without_validationを使えば良い
alias_method_chainの衰退
- validationしたくないsaveをするのであればsave_without_validationを使えば良い
- alias_method_chainはRailsの中でメソッドのリネームやシャッフルを繰り返した結果、どのバージョンを使っているのかわからなくなってしまう問題があった
- オブジェクト指向には別のモジュールにメソッドを定義し、元のモジュールをインクルードするという使い方ができる
- alias_method_chain を使わずともsuperを用いて周囲に機能を追加することができる
Module#prepend の誕生
- 継承チェーンの高い位置にあるメソッドをオーバーライドするモジュールを使いたい場合superで呼び出すことができない
- この時中間モジュールを作り継承チェーンに差し込むことで実現できるが、Railsのライブラリなど、直接コードを触りたくない場合もある
- Module#prepend を用いることで継承チェーンの下に目的のモジュールを差し込むことで解決している
12章 アトリビュートメソッドの進化
- ActiveRecordはカラムをみて
カラム=
,カラム?
などのアクセサメソッドを自動的に生成する - はじめはmethod_missingを使ったゴーストメソッドだったが、存在しないメソッドを用いると継承チェーンを1周して探してしまう
- 動的メソッドを用いて問い合わせ用のアクセサを全て作成するという方法もあったが、Railsはそれらを両方取り入れた
- はじめにmethod_missingを呼び、その後にdefine_attribute_methodを呼び出す
- 2回目以降はゴーストメソッドではなく、実体を呼び出せるようになる
- その後のRailsではアトリビュートメソッドはさらに改善されている
- アトリビュートアクセサを定義する時にUnboundMethodに変更してからキャッシュに保存
- 別のクラスが同じアトリビュート名で同じアクセサを必要とした場合これを使い回している!
13章 最後の教訓
- メタプログラミングはただのプログラミング
メタプログラミングRuby読みメモ 5
6章 コードを記述するコード
Kernel#eval
Kernel#eval
は、ブロックではなくRubyのコードの文字列を受け取る- これをコード文字列と呼ぶ
- eval は渡されたコード文字列を実行する
- instance_evalとclass_evalもブロックだけでなく、コード文字列も受け取れる
Bindingオブジェクト
- Binding はスコープをまとめたもの
- Binding を使ってローカルスコープを取得すればそのスコープを持ち回すことができる
Kernel#binding
メソッドで生成できる- スコープを持っているため、evalでBindingオブジェクトに渡すことでそのスコープでコードを評価できる
- すごい!こんな仕組みだったんだ
- pryというgemにはオブジェクトのスコープでインタラクティブセッションを開く
Object#pry
メソッドが定義されている - これを用いてbindingのpryを呼び出すことでデバッガのように使うことができる
irb
- irbは標準入力やファイルをパースして、それぞれの行をevalに渡すプログラム
- こうしたプログラムはコードプロセッサと呼ばれる
eval の問題点
- コード文字列はシンタックスハイライトが効かない
- 外部からのコード文字列を受け取るとコードインジェクションの危険がある
オブジェクトの汚染とセーフレベル
- 潜在的に安全ではないオブジェクトに自動的に汚染の印がつく
- 汚染オブジェクトには、ウェブフォーム、ファイル、コマンドライン、システム変数、プログラムが読み込んだ文字列などが含まれる
- 汚染された文字列を操作して新しい文字列を作ると、その新しい文字列も汚染される
- taind?メソッドで確認できる
- Ruby はセーフレベルを設定できる
- セーフレベルが0より大きいと汚染した文字列を評価できない
フックメソッド
- イベントをキャッチできるメソッド
Class#inherited
は継承されたときに実行してくれるメソッド- デフォルトでは何もしないのでオーバーライドして使う
Module#included
やModule#prepended
でモジュールのライフサイクルにプラグインできるModule#extended
でモジュールを拡張したときに入り込める- 特異メソッドをキャッチするときには
Kernel#singleton_method_added
などを使う
メタプログラミングRuby読みメモ 4
5章 クラス定義
- Ruby の class キーワードはオブジェクトの動作を規定しているだけでなく、実際にコードを実行している
- クラス(やモジュール)定義の中ではクラスがカレントオブジェクトselfになる
カレントクラス
- Ruby は常にカレントオブジェクトselfを持っている
- 同様にカレントクラス(およびモジュール)も持っている
- メソッドを定義すると、それはカレントクラスのインスタンスメソッドになる
- class キーワードでクラスをオープンするとカレントクラスになる
- メソッドの中ではカレントオブジェクトのクラスがカレントクラスになる
class_eval
Module#class_eval
はそこにあるクラスのコンテキストでブロック評価する- class_eval は self とカレントクラスを変更する
- class キーワードは定数を必要とするが、class_evalはクラスを参照する変数ならなんでも使える
クラスインスタンス変数
- インスタンス変数はカレントオブジェクトselfに属しているもの
- クラス定義の中ではselfはクラス自身となる
クラス変数はクラスインスタンス変数と異なり、クラスのインスタンスやサブクラスからもアクセスできる
- main で定義したクラス変数はObjectで定義されるのでObjectを継承した他のクラスで同名のクラス変数を用いると上書きされてしまう罠があったりする
特異メソッド
- 単一のオブジェクトのみもつメソッド
- 追加しても他の同じクラスのオブジェクトに影響はない
- クラスメソッドはクラスの特異メソッド
クラスマクロ
- Ruby にはアトリビュートがないが、
Module#attr_*
を使うと読み書きのメソッドを生成できる - この attracts_accessor などのようなメソッドをクラスマクロと呼ぶ
- キーワードのように見えるがクラスメソッドである
特異クラス
- オブジェクトはクラスではないのでメソッドを持つことはできない
- オブジェクトは自身のクラスとは別に特別なクラスを持っており、特異クラスと呼ばれる
- 特異メソッドはここに存在する
- クラスメソッドはクラスの特異クラスが持つメソッドである
- 特異クラスのスーパークラスはスーパークラスの特異クラスの関係
- 特異クラスはクラスであるため特異クラスも特異クラスを持つ
Rubyのオブジェクトの7つのルール
メタプログラミングRuby読みメモ 3
4章 ブロック
- ブロックはスコープを制御するのに強力なツール
- ブロックは「呼び出し可能オブジェクト」
- Proc や lambda などが含まれる
ブロックの基本
- ブロックは、波括弧またはdo...endキーワードで定義できる
- 1行のブロックには波括弧、複数行のブロックにはdo...endを使うという習慣がある
- ブロックを定義できるのはメソッドを呼び出すときだけ
- ブロックはメソッドに渡され、メソッドはyieldキーワードを使ってブロックをコールバックする
- ブロックは引数を受け取ることもできる
- ブロックをコールバックするときに、メソッドを呼び出すときと同じように引数を渡せる
- メソッドの内部で
Kernel#block_given?
メソッドでブロックの有無を確認できる
ブロックはクロージャ
- ブロックのコードは単体では実行できない
- ローカル変数、インスタンス変数、selfといった環境が必要
- これらはオブジェクトに紐づけられた名前のことで、束縛とも呼ばれる
- ブロックにはコードと束縛の両方が含まれる
- ブロックを定義すると、その時点その場所にある束縛を取得する
- ブロックをメソッドに渡したときにその束縛も一緒に連れて行く
- ブロックの中で新しい束縛を定義することもできるが、ブロックが終了した時点で消える
- このような特性からブロックはクロージャとも呼ばれる
スコープ
- Ruby のスコープはきちんと区別されている
- 新しいスコープに入ると以前のスコープは見えなくなる
- メソッドから同じオブジェクトにある他のメソッドを呼び出すと、インスタンス変数は変わらない
- グローバル変数はどのスコープからでもアクセスできる
- トップレベルのインスタンス変数は、他のオブジェクトがselfになるまで呼び出せる
スコープゲート
- プログラムがスコープを切り替えて新しいスコープをオープンする場所は3つある
- クラス定義
- モジュール定義
- メソッド
- これらの境界線は
class
,module
,def
といったキーワードで印つけられている この3つのキーワードは、スコープゲートとして振る舞う
class の代わりに Class.new で定義するとスコープを切り替えずに定義できる
- def の代わりに define_method を使うとスコープを切り替えずにメソッドを定義できる
instance_eval
BasicObject#instance_eval
に渡したブロックはレシーバをselfにしてから評価される- インスタンス変数やprivateを扱える
- instance_evalに渡したブロックのことをコンテキスト探査機と呼ぶ
- これを用いて、irbからオブジェクトの中身を見たいときに使うことができる
クリーンルーム
呼び出し可能オブジェクト
Proc オブジェクト
- Ruby ではほぼ全てがオブジェクトだがブロックはオブジェクトではない
- ブロックを保管しておいて後で実行したいときにオブジェクトが必要
- Proc はブロックをオブジェクトにしたもの
- オブジェクトになったブロックを評価するには
Proc#call
を呼び出す - Proc は遅延評価である
- ブロックをProcに変換するカーネルメソッドはlambdaとprocと2つ用意されている
&修飾
- ブロックはメソッドに渡す無名引数のようなもの
- 通常はyieldを使って実行するが、yieldでは足りないケースが2つある
- 他のメソッドにブロックを渡したいとき
- ブロックをProcに変換したいとき
- メソッドの引数列の最後に&修飾した名前を置くとブロックを束縛できる
- これはProcとして変換されている
lambda
- lambda で作られた Proc は他の Proc とは違う
Proc#lambda?
で確認できる- lambda の return は lambda から戻るだけだが、Proc での return は Proc が定義されたスコープから戻る
- lambda の方が Proc よりも引数の値に厳しい
- Proc は引数の数が多いと切り落とし、少ないとnilを入れてくれる
- lambda では ArgumentError となる
Method オブジェクト
Object#method
でメソッドそのものをオブジェクトとして取得できる- to_proc で Proc に変換できる
- lambda は定義されたスコープで評価されるが、Methodは所属するオブジェクトのスコープで評価される
UnboundMethod
- 元のクラスやモジュールから引き離されたメソッド
Method#unbind
でMethodから変換できる- instance_methodを呼び出せばUnboundMethodを直接取得できる
- UnboundMethodを呼び出すことはできないが、ここから通常のメソッドを生成することができる
UnboundMethod#bind
で束縛することができる- ただし、元のクラスと同じクラス、もしくはサブクラスにしか束縛できない
メタプログラミングRuby読みメモ 2
3章 メソッド
重複問題
- コードの重複は間違いを犯すかのせいをはらむ
- Ruby はいくつか解決方法を持つ
動的メソッド
- メソッドを動的に呼び出す
- メソッドを呼び出すには通常はドット記法を使う
Object#send
を使って呼び出す方法もある- send の第一引数はオブジェクトに送信するメッセージ、つまりメソッド名である
- 送るメソッド名にはシンボルまたは文字列が使える
- send を用いることで、呼び出したいメソッド名が通常の引数となり、実行時に呼び出すメソッドを指定できる
- これを
動的ディスパッチ
と呼ぶ - send メソッドは privateのメソッドすら呼び出すことができる
- メソッドを動的に定義する
Module#define_method
を使うと、メソッドを動的に定義することもできる- メソッド名とブロックを渡し、ブロックがメソッドの本体となる
- define_methodが実行されたクラスのインスタンスメソッドとして定義される
- これを
動的メソッド
と呼ぶ
method_missing
- Ruby にはメソッド呼び出しを制限するコンパイラは存在しないため、存在しないメソッドも呼び出すことができる
- メソッドが見つからなかった時、Rubyは
BasicObject#method_missing
を呼び出す - method_missing は private だが、send を使えば呼び出すこともできる
method_missing は NoMethodError を出す
NoMethodErrorってこういう仕組みだったんだ...すごい
method_missing をオーバーライドすれば受け取ったメッセージを捕まえることができる
- ブロックが渡されていればそれも捕まえることができる
- method_missing をオーバーライドすることで、実際には存在しないメソッドを呼び出せる
- method_missing でのバグは潰しにくい
- method_missing をオーバーライドしているため NoMethodError を出せずに無限ループしてしまうこともある
ゴーストメソッド
- 同じようなメソッドをたくさん定義しなければいけないときには、いちいち定義せずにmethod_missingの呼び出しに反応すればいいこともある
- ここで処理されたメッセージは呼び出し側からは通常のメソッドのように見えるが、レシーバー側には定義されていない
- これをゴーストメソッドと呼ぶ
- ゴーストメソッドを補足して、他のオブジェクトに転送するオブジェクトを動的プロキシと呼ぶ
- respond_to_missing?をオーバーライドすればrespond_toにも反応できるようになる
- 常に super を呼び出す、respond_to_missing?を再定義することを基本に実装する必要がある
- methods?の結果一覧に登場しないなどの通常のメソッドとの振る舞いの違いがある
「可能であれば動的メソッドを使い、仕方がなければゴーストメソッドを使う」
ブランクスレート
- ゴーストメソッドと継承した本物のメソッドの名前が衝突すると、後者が呼び出される
- 継承したメソッドが必要ない場合は削除すれば衝突を解消できる
- 名前の衝突を防ぎたいのであれば、不要メソッドは削除するべき
- undef_method で削除できる
- 最小限のメソッドしかない状態のクラスをブランクスレートと呼ぶ
- Rubyにはこのブランクスレートが用意されている
- それがBasicObjectである