30日でできる! OS自作入門 を読むために nasm_of_nask というコンパイラを作った話
この記事は はてなエンジニア Advent Calendar 2020の22日目の記事です。 昨日はid:motemen さんの PCを離れたらマイク音量を下げるmacOSアプリを作った - 詩と創作・思索のひろば でした!
経緯
とこぼしたところ、著者の 川合秀実 (@hkawai3) | Twitter さん*1 を始めとした各種大先生に目をつけられて*2しまい、やらなければ肉体が低レイヤから順に消されると思い読み始めました。一ヶ月に一回、自分の知的好奇心を満たすのは自作OSなんじゃないかなって思う時があるんですけど最近その頻度が早まってきた
— この人生が裏目りやすい!2021 (@Krout0n) 2020年12月17日
しかし、この本の初版は2006年であり時代はフロッピィ〜〜〜〜〜〜から†SSD†なわけで、macOS上で動かすための環境構築はqemuを使ったり、本の中で使われているツールのmacOS版を用意する必要があります。
このように、以前までは有志が用意をしてくれていたので何とかなっていました。あのOSが出るまでは。
macOS Catalina
macOS Catalinaからついに32ビットアップリが使えなくなりました。これの影響で上記のリポジトリで提供されているnaskを使おうとするとbad CPU type in executableするわけです。
代替手段としてのNASM
naskの代替手段としてはNASMを使ったりすると良いらしいです。しかし、naskとNASMはコンパチではないのでいくつかサンプルコードを修正する必要があります。
『30日でできる!OS自作入門』を macOS Catalina で実行する - Qiita から修正する表を引用したものがこちらです。
nask | NASM |
---|---|
RESB 18 | TIMES 18 DB 0 |
RESB 0x7dfe-$ | TIMES 0x1fe-(−−$) DB 0 |
ALIGNB 16 | ALIGN 16, DB 0 |
[FORMAT "WCOFF"] | この行削除 |
[INSTRSET "i486p"] | この行削除 |
[FILE "naskfunc.nas"] | この行削除 |
[FORMAT "WCOFF"] | この行削除 |
JMP entry | JMP SHORT entry |
_io_hlt( _ の付くもの) | io_hlt( _ を削除) |
な〜〜〜んだこれだけなんだ!簡単ですね!!!
しかし
この表を一々頭に覚えるのはめんどくさい、めんどくさいですよね?*3いっそのことnaskを作り直すのもいい*4が、多分その時間でこの本読み切れるな、という葛藤がありました。そこで僕なりのアプローチとして 「「「nask のコンパイリ*5を受け取って NASM にコンパイルするだけのめちゃくちゃ簡単なコンパイラ」」」を書けばいいんじゃないか?と思って用意したものがこちらになります。
nasm_of_nask
最近は趣味でocamllexとmenhirばっか書いてたし、OCamlと仲良くなりたかったので実装言語に選びました。本質っぽいところはここでそれ以外はまんまを吐き出す本当にやるだけのコンパイラです。命名規則は OCaml の型変換関数 *6 をリスペクト*7しました!ℒℴѵℯ...
対応状況
対応してる共通部
naskとNASMの差分
nask | NASM | 対応してる? |
---|---|---|
RESB 18 | TIMES 18 DB 0 | ○ |
RESB 0x7dfe-$ | TIMES 0x1fe-(−−$) DB 0 | ○ |
ALIGNB 16 | ALIGN 16, DB 0 | × |
[FORMAT "WCOFF"] | この行削除 | × |
[INSTRSET "i486p"] | この行削除 | × |
[FILE "naskfunc.nas"] | この行削除 | × |
[FORMAT "WCOFF"] | この行削除 | × |
JMP entry | JMP SHORT entry | × |
_io_hlt( _ の付くもの) | io_hlt( _ を削除) | × |
Future work
- 対応増やす
- CIとかでいい感じにバイナリをビルドする
- これから始める全人類がnaskの代わりにこれとnasmを使うコバンザメ戦法したい
締め
気づいた方は5000人ぐらいいらっしゃるとは思いますが、id:Krouton は1日目で出てくる差分しかやっていません。何故かと言えばまだ1日目しか読んでない*8からです!ガハハ!
オチもついたところで解散とします!明日の担当はid:pokutunaさんです!!
*1: 川合さんへ: 1年以上関わりがあるのにずっと読む読む詐欺しててすみません!!!!!
*2: ふぁぼられただけです、所でふぁぼって死語ですか?
*3: この記事書いてから気づいたんですけど、そんなに多くないな...
*4: DB命令だけ使えるnaskなら書けそう
*5: この人生が裏目りやすい!2021 on Twitter: "caller-callee-callの関係を考えればコンパイラ-コンパイリ-コンパイルの関係も自明"
*6:OCamlプリミティブな型変換関数は to_of_from といった命名規則が多い。例えば int から char だったら char_of_int みたいな
*7: 世の中にはOCaml から JSを吐く処理系もある GitHub - ocsigen/js_of_ocaml: Compiler from OCaml to Javascript.
*8: 参加してる読書会が2つあるのに気づいてそっちをやらないと本当にまずい
whywaitaはフッ軽
この記事は whywaita Advent Calendar 2020 の 22日目の記事です! 21日目の記事は id:yu_ki_kun_0 さんが担当でした!
ここ2ヶ月くらい whywaita と僕はラーメンをよく食べに行きます。そのうち2回とも当日の夕方に「行きたいね〜」「今日行くか!」となってる感じです。
1回目
ラーメン富士丸 明治通り都電梶原店 ラーメン普通 アブラ別皿 ニンニクマシ アブラの味が頭にガツンときて最高!!!惜しむらくはブタがカチカチだったところか、とはいえ味がして最高…… pic.twitter.com/DXWvwWqhxJ
— why/橘和板 (@whywaita) 2020年11月13日
whywaita〜〜〜〜〜 (@ ラーメン富士丸 明治通り都電梶原店 in 北区, 東京都 w/ @whywaita) https://t.co/ioyJMoW5Nm
— 出生(eスポーツ) (@Krout0n) 2020年11月13日
仕事終わりに渋谷から電車で40分かかる東京の端の方の梶原*1まで来てくれました! まあ僕は横浜から梶原*2まで行ったんですけどね!!!!! その後はHUBに行ったので本当に二郎からのセイクで優勝ですね!
食べに行ったお店
2回目
ごちそうさまでした〜 (@ らーめん玄 in 世田谷区, 東京都 w/ @whywaita) https://t.co/7HM26GH6Pe
— 出生(eスポーツ) (@Krout0n) 2020年12月17日
食べに行ったお店
まとめ
この2回のラーメン*3から分かるようにwhywaitaさんはフッ軽*4なので例えば明後日のクリスマスイブやクリスマス当日、大晦日やお正月に寂しい方は誘ったら来てくれます*5!是非whywaitaと楽しい時間を!
明日の担当は id:kadokusei です〜
アイキャッチ用
Kroutonの睡眠を支えるほげほげ
この記事は whywaita Advent Calendar 2020 - Adventar 11日目の記事です
前回は kyontan の ICTSCのコンテストサイトを支えた技術 でした! 飛行機乗りたいけど免許も取りたいしそもそも机とかも欲しいので優先度ガタ落ちしてます!!!!ちなみにあんま元気じゃないです、kyontanさんは卒業できそうですか??
本筋
ところでみなさん睡眠してますか?してない方は成仏してくださいませ。僕は睡眠は好きなのですが、如何せん下手くそです。長く寝れないし寝付きもまともにできない...、車の中はもちろん、飛行機、新幹線や電車(グリーン車含む)でも寝れないし、旅行先の寝具が違う環境になるだけで寝れなくなります。
旅先
レム睡眠3割!!!旅行好きなのにホテルだと睡眠が大抵しんどいので困った...。大体悪夢見ますよね旅行先だと、前は歯が全部抜ける夢見ました。
自宅で普通に寝れた時
深い眠りが短く下手くそなのは今後の課題なのですが、それはそれとして前は自宅でも寝付くことができませんでした。
そんな自分が多少は寝付けるようになった色々を紹介していきたいと思います。
枕 ロフテーキューブ(羽毛) 4号*1
社会人になってから真っ先に買ったものです。肩幅デカい族なので4号が良い。
他のメーカーの枕をあまり試していないのですが、とりあえず横浜の実店舗に行って試せたのがめっちゃプラス。店員さんが布団の硬さや自分の骨格と好みに合わせて選んでくれるのでとても助かる。
加重毛布
これがとても良い。このように全てを破壊してしまくらい寝相悪いので朝起きたら毛布無いなんてザラ。
これ被せてから起きた時に毛布かぶってる率が80%くらいまで上がった。その代わり二度寝率も上がったけど。
メラトニン
はい、メラトニンのサプリメントです。多分19時とか20時くらいに飲むといい感じに眠くなるのですが、ゲームやコーディングしてるとすぐ飛んじゃうので結局それらが終わってからいつも飲んでます。
商品より送料のほうが高いし、60個入りのものを1つずつしか税関の都合上買えないのが難点。後述する薬を飲み始めるまではとても頼りにしていました。
リーゼ(クロチアゼパム) 5mg
ついに出てきました、精神科/心療内科などで処方されるお薬です。寝る前に飲んでいます。不眠とは別で通っているのですが、そこそこ催眠作用が強い割に依存性が強くないのでいいですね。前は朝にも別の薬と同時に服用してましたが本当に仕事にならないので止めました。
飲酒をしない
上2つのサプリメントと薬を服用し始めてからかなり減らしました。飲むにしても早い時間とか、入眠失敗してもいい日だけ飲むようにしてます。もっとお酒飲みたい〜〜〜〜〜〜〜
結び
睡眠は大事にしましょう、明日は id:nersonu さんの当番です!!!!
追記 (2020/12/11 16:06)
Kroutonの睡眠を支えるほげほげ - KRAZY感情STYLE枕良さそう.今使ってる枕なんか合ってない気がするので気になる.セニラン飲んでるのでだいたい仲間っぽい.ところで睡眠測定するアプリなんてやつですか
2020/12/11 11:41
Xiaomi の mi band 5 ってスマートウォッチをつけて寝てます!
*1:画像は公式サイトから引用
ISUCON10にTrust Rustチームとして参加して予選敗退しました
てがきはてなブログがリリースされた日に書いたんですけど誤操作で消しちゃって萎えてましたが再度書きました。一緒に出てくれてた id:dekokun と id:stefafafan には感謝です。僕は実装を読んでゴリゴリやる役目だったのに筋力が足りなくて全然出来ませんでした、来年はもっと強くなります。
チームメイトのエントリ
具体的に何したとかはチームメイトの奴を読んだほうが早いです
std::sync::mpsc::channelで少しハマったのでメモ
std::sync::mpsc - Rustの挙動についてちょっと知見を持ったので綴っていく。
やりたいこと
めっちゃ重たいタスクがN個あって、それぞれにスレッドを作ってmpsc::channel
経由でメインスレッドに値を返してもらう例を考える。
use std::thread; use std::sync::mpsc; let (tx, rx) = mpsc::channel(); for costly_task in costly_tasks.into_iter() { let tx = mpsc::Sender::clone(&tx); thread::spawn(move || { let val = costly_task(); tx.send(val).unwrap(); }); } for val in rx { dbg!(val); }
これだと実は下のfor文を抜けることは出来ない。実際にplaygroundでやると強制的にkillされていることが確認できる。
ではどうするのか?というと for文の前で drop(tx);
を呼んであげる必要がある。
for costly_task in costly_tasks.into_iter() { // ... } drop(tx); for val in rx { // ... }
僕みたいになぜ?となった方へこの問題をもう少し簡単にしてみる。ならなかった人はスター押してはてブ追加して頂ければ大丈夫です。
簡単にした例
use std::sync::mpsc; let (tx, rx) = mpsc::channel(); let tx1 = mpsc::Sender::clone(&tx); tx1.send(1).unwrap(); for val in rx { dbg!(val); }
これも同様にfor文を抜けることが出来ない。というわけで drop(tx); drop(tx1);
を追加してみると同様に抜けることができる。
どうやらfor _ in rx { ... }
はsenderが値が送りきるのを待つ挙動をしているようだ、とのことでドキュメント 1 を確認しに行く。
Returns an iterator that will block waiting for messages, but never panic!. It will return None when the channel has hung up.
うんうん、推測は当たっていたようだ。Sender::send
は &self
なので、1回値を送っただけではdropしない。なので、rx
はtx
達が生きていることが分かっているので明示的にdrop
してあげないとforから抜けれなかったわけか。
ということは
use std::thread; use std::sync::mpsc; // このtxは使ってないけど明示的にdropさせる必要がある let (tx, rx) = mpsc::channel(); for costly_task in costly_tasks.into_iter() { // ここの tx は for が回るたびにスコープから抜ける => drop する let tx = mpsc::Sender::clone(&tx); thread::spawn(move || { let val = costly_task(); tx.send(val).unwrap(); }); } // 明示的にdropしてあげる drop(tx); // 全部のSenderがdropしているのでこのforはちゃんと抜けられる for val in rx { dbg!(val); }
ってわけですね、少しハマったのでメモです
宣伝
今回の挙動を確認したりドキュメントを読んだりするのにrust-jpのSlackで質問を投げていました。活発なコミュニティで優しい方が多いので皆さん入りましょう!!
-
本当は
into_iter
なんだけどなかったしiter
でも同じでしょう↩