VRM as a Code - またはコードによりあなたのアバターを定義する方法について

下書きのまましばらく放置されたのでインターネットの海に放流

目標

hikaliumのアバターをコードで定義して、GUIを用いることなく服を着せ替えたりパラメーターを操作するなどできるようにしたい

先行研究

背景情報

VRM形式

人型アバターで最近主流になりつつあるファイルフォーマット。glTF形式をベースにしている。

glTF形式

調査

VRM/glTFを理解する

bin領域のデータのエンディアン

Little Endianにしなければならない(MUST)

All buffer data defined in this specification (i.e., geometry attributes, geometry indices, sparse accessor data, animation inputs and outputs, inverse bind matrices) MUST use little endian byte order.

UV map

https://docs.blender.org/manual/ja/dev/addons/import_export/scene_gltf2.html#uv-mapping

KHR_texture_transform

Animation

three-vrm example の humanoidAnimation のデモがデバッグに使えそう

実装

github.com

とりあえずVRMをglTFとして読み込んだら、テクスチャ付きでprimitive単位に分割して再出力することはできた。

また気力が湧いたら続きをやろう。

2022年の振り返り

例年通り、今年あったことの概要をまとめておく。

1月

3Dプリンターをお買い上げしてた(今年だったんだ…)

ちなみにブログ記事も書いた

hikalium.hatenablog.jp

2月

3Dプリンタメンガーのスポンジを印刷してた(筆記用具は商品には含まれません)

そしてhikaliumとして初の社のイベント登壇をした

そしてもう一台3Dプリンタが増えた(錯乱)

戦争とかが始まったのもこのあたりらしい(世も末ですな)

あまりにも眠れなさすぎる現状を歌にするなどした(かっとしてつい…聴かなくていいですよ!!!)

3月

情報処理学会のOS研究会で社の仮想ブースを回すなどしていた

新しいお友達もお迎え(コアスイッチとして現在も頑張ってくれています)

おうちNOC業の様子

車輪を再発明した(車輪ではなく車(輪がついた物体の進入を防ぐ)輪)

技術的な方法でTwitterの閲覧時間を削減する試みを開始(まあまあ役に立っており、今もつかっている)

4月

社の公式ブログでお仕事に関する記事を書いた

Redox を読む会を始めた(次回をスケジュールするのを忘れていました…来年やります!)

セキュキャンのCコンパイラゼミが、元受講生の講師を迎えて再始動することが決定(本当にめでたかった!)

ZOZOMATを試すなど(足、印刷しなきゃ…)

ちなみにイエベ春らしいです(ZOZOGLASS曰く)

社のイベントで人々に自作OSはいいぞ!をするなどした

スライドはここ

そして Asahi Lina さんとコラボした!(Linaさん、つよつよなので尊敬している)

Linaさんのチャンネルにお邪魔して、私が自作OS語りをしてくる回もありました!

5月

kurenaif魔女のチャンネルにKOBA789氏と一緒にお邪魔して、DirtyPipe ( CVE-2022-0847 )を完全理解した

そして弊チャンネル初のSuperThanks受領(ありがとうございます!)

思い立ったのでosdev-jpのcode of conductを制定するなどした

2月に公開した歌の解説記事を書いた(解説の必要な歌って、なに?)

3Dプリンタがあるなら、ということで3Dスキャナに入門した(むずかしい)

USB Type-C の電気的な側面の理解を試みるなどした(少し理解した)

そして!はじめての!オフィスでの!夕食!(入社2年半以来初の快挙)

分解衝動の発散

6月

計算資源が追加された

ふるさと納税メロンを摂取(ひとりで2個まるごと食べるのはなかなかの難易度だった…超美味しかったけど…)

ついにToDoリストアプリをつくるというToDoに着手(現在メンテ中で落ちてる)

7月

Rust for Linuxでお遊びする配信をするなどした

[大人の]科学を大人買いするなど

そしてRP2040を飛ばす

そして破壊衝動の発散(ではなく異音の原因を除去した)

ちゃんと原因特定している図

他の物体も分解(分解ばっかりしてるな)

Optaneはもうおしまいです、わたしは悲しいよ…(終わらせないぞ!!!)

活版印刷入門

8月

夏はやっぱりセキュキャン!自作OS!だよね!!

あと電源は大事です。電源は大事なので。

インターンの人々の優しい言葉に救われました。本当にありがとうございます!!!

そして深淵を覗く

9月

はじめての入院入門!

ハードウェアアップデートのためしばらくオフラインに

生還したので辞世の句にはならなかった歌ってみた動画

2週間の入院後なんとか帰宅したら家は無事ではなかった話(またエアコンから水かよ(自力でなんとかしました))

リコリコに影響されて「力」を手に入れるなど(もちろん合法な力です)

ikaliumです(入院中に発売されていたスプラ3に入門)

10月

低レイヤーガール、チャンネル登録者数4000人突破!!(ありがとうございます!)

二次元セルオートマトンを一発実装する THE FIRST CODE をやってみたら、しょうもないコンパイルエラーで終了した件(ちゃんと後でデバッグしました!)

そして新しい物理アバターのmetrics exporter(丸くてかわいいのでお気に入りです)

久々に物理世界のイベントに参加して自己肯定感が向上した図(hikaliumは優しい皆様に救われています)

11月

情報公開(伏線回収)

やさしい世界

そしてDNSの浸透

3年間お預けだった、リアルワールド開催のSTEP Class 8に参加してきた!(mallocチャレンジの参加者の何名かに賞を差し上げました!)

テプラSR5900Pのプロトコルを解析して、CLIから印刷できるツールを実装した

物質、いいよね(用法用量を守って正しく使いましょう)

12月

ポケモンSV、はじめました!

Open Source Summit 聴講(社の人間としては初の物理カンファレンス参加)

WebUSBに入門した(自作OSでドライバ実装したときの大変さに比べたらマジで拍子抜けするほど簡単でびっくりした、みんなもっとこれで遊ぼう!)

/sys/hikalium/tracing を生やした(デバッグ用なのでノイズが多いですが興味あればどうぞ)

hikalium.hatenadiary.jp

胸に刻んでおきたい言葉(手前味噌ですが)

hikaliumのロゴの背後の深層心理はこれでは?という発見

no (クリスマス) spanning-tree

大掃除

著者近影

総括

振り返ってみれば、想像以上にたくさんのことをやっていたことに気づいた。もっと自信を持って欲しい > 自分

ついに情報公開も果たしたが、誰もが何事もなく受け止めてくれて、人々の優しさを再確認した。これからは、以前にも増して、自分の「好き」をもっと大切にしてゆきたいと思う。(みなさんもぜひ、そうしてくださいね!)

おしごとに関しては、外から見える範囲のCommunity contributionをたくさんできたのは素晴らしかったし、これからも続けてゆきたいが、もっと強くなって技術的な成果のほうもさらに挙げてゆきたい。問題は、時間が足りないというところだが…(生き残りたい!!!)

あと、もう少し精神を安定させてゆきたいですね。もっと、ゆっくり、やっていこう。活動を抑え気味にしているつもりでも、これだけのことをできているのだから。

今年お世話になった皆様におかれましては、本当にありがとうございました。

来年以降も引き続き精進してまいりますので、hikaliumをどうかよろしくお願いいたします!!!

Rustで自作OSをしているときのデバッグ例 - syscall 命令と仲良くなりたい!後編

この記事は自作OS Advent Calendar 2022の18日目の記事です。前回のあらすじはこちら:

hikalium.hatenablog.jp

例外を吐いてるのはどこのどいつだぁ?

さて、前回の状況をおさらいすると、

  • 0x200fd0の命令がアドレス0x2000000000にアクセスしようとしてページフォルト(0xe)を発生させた。
  • ページフォルトを処理している最中に一般保護例外(0xd)が発生し、一般保護例外を処理中にもさらに一般保護例外が発生することで、順次スタックが食い潰されていった。
  • 例外ハンドラのスタックが枯渇し、0x3de4bed8にアクセスしようとした段階でページフォルトが発生した。これにより、ダブルフォルトが発生した。
  • ダブルフォルトハンドラ内で再度ページフォルトが発生した結果、トリプルフォルトとなってリセットがかかった。

というわけで、まずは最初のきっかけである 0x200fd0とかにある命令がどこのどいつかを特定することにしましょう。

まず、アプリのロード状況を確認します。OSのログを見ると、

Executable found: Elf { name: hello, data: @0x000000003e2d9000 } 
This ELF seems to be executable!
phdr_start: 64, phdr_entry_size: 56, num_of_phdr_entry: 5
type  : (rwx) = (101)
offset: 0x0000000000000000
vaddr : 0x0000000000200000
fsize : 0x0000000000001B64
vsize : 0x0000000000001B64
align : 0x0000000000001000

type  : (rwx) = (110)
offset: 0x0000000000001B68
vaddr : 0x0000000000202B68
fsize : 0x00000000000001C8
vsize : 0x00000000000001C8
align : 0x0000000000001000

load_region_start = 0x0000000000200000
load_region_end = 0x0000000000203000
load_region @ 0x000000003d233000
dst_range: 0x0000000000000000 - 0x0000000000001B64
dst_range: 0x0000000000002B68 - 0x0000000000002D30
run the code!
entry_point = 0x000000003d2332f0

というふうになっています。これはどういうことかというと…図示したほうがはやそうなので、お絵描きしました。

現在のアプリケーションのロードの様子

現在は手抜きをするために、アプリケーションとOSのメモリ空間を分けていないので、アプリの希望通りのメモリレイアウトではなく、相対位置を維持したまま、異なるアドレスにCodeとDataの両方をロードしているのでした。

さて、例外を発生させたメモリアドレス(0x200fd0)は、このロードされたコード(0x3d233000-0x3d234b64)の中…にありませんね…あれ?

…と思って状況をよくよく確認すると、どうもこのアドレスは、アプリがもともと予期していた、コード領域がロードされるべきアドレス範囲(0x200000-0x201b64)の中にあるようです…つまり、アプリケーションが正しく再配置可能ではなかった、もしくはローダーが仕事をサボっているせいで、正しく再配置できなかった、のいずれかの可能性であると考えられます。ちょっとアプリのバイナリのほうを確認してみましょう。

$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -A 3 200fd0
0000000000200fd0 <<core::fmt::Arguments as core::fmt::Display>::fmt>:
  200fd0:       48 83 ec 38             sub    $0x38,%rsp
  200fd4:       48 8b 06                mov    (%rsi),%rax
  200fd7:       48 8b 76 08             mov    0x8(%rsi),%rsi
  200fdb:       48 8b 4f 28             mov    0x28(%rdi),%rcx

objdumpに-Cオプションをつけると、シンボルをデマングル(人間が読みやすい形式に変換)してくれるみたいで、Rustのシンボル名にも対応してるみたいです。ありがとうbinutils!

というわけでですね、この200fd0は、明らかにDisplay::fmtを呼び出しているコードで利用されていて、我々のprintln!のコードパスで通っていそうな雰囲気がひしひしと伝わってきます。

さて、これを呼び出している側のコードはどうなっているのでしょうか?rip相対のcallではこのようなことは起こり得ないので、レジスタ間接でのcallがDisplay::fmtへ制御を移そうとしているはずです。なので、レジスタ間接callを使っている人々をリストアップしましょう。

$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -E -e 'call.*\*' -e ' <.*>:$' | grep 'call.*\*' -B 1
0000000000200860 <<core::panic::panic_info::PanicInfo as core::fmt::Display>::fmt>:
  200886:       41 ff 56 18             call   *0x18(%r14)
  2008bf:       ff 50 18                call   *0x18(%rax)
--
0000000000201030 <core::fmt::write>:
  2010b7:       ff 50 18                call   *0x18(%rax)
  201177:       ff 54 08 08             call   *0x8(%rax,%rcx,1)
  2011d8:       ff 50 18                call   *0x18(%rax)
  2011e9:       42 ff 54 35 08          call   *0x8(%rbp,%r14,1)
  20122f:       ff 51 18                call   *0x18(%rcx)
0000000000201250 <core::fmt::Formatter::pad_integral>:
  2013f1:       ff 55 20                call   *0x20(%rbp)
  20150a:       41 ff 55 20             call   *0x20(%r13)
  201557:       ff 55 18                call   *0x18(%rbp)
  201582:       41 ff 55 18             call   *0x18(%r13)
  2015aa:       41 ff 55 20             call   *0x20(%r13)
00000000002015d0 <core::fmt::Formatter::pad_integral::write_prefix>:
  2015f1:       41 ff 54 24 20          call   *0x20(%r12)
0000000000201630 <core::fmt::Formatter::pad>:
  2018ea:       41 ff 56 20             call   *0x20(%r14)
  20190d:       41 ff 56 18             call   *0x18(%r14)
  20193f:       41 ff 56 20             call   *0x20(%r14)

まあ…たぶんcore::fmt::writeですよね…きっとそうだ…

`brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep '<core::fmt::write>:' -A 155 | grep -z -E -e 'call' --color='always'

qemu/target/i386/cpu.h

$ cat qemu_debug.log | grep hikalium | head -n 3
hikalium: raise_interrupt2: is_int == 0 intno = 14 eip=0x000000000000200FD0 error_code = 0x000000000000000000
hikalium: rsp: 00000000003E286BE0
hikalium: ret_to: 00000000003D2341EE
$ cat com2.log | grep -a -e entry_point
entry_point = 0x000000003d2332f0
$ make objdump_hello | grep '<entry>:'
00000000002002f0 <entry>:
$ ./scripts/debug/inspect_app_crash.sh 
0000000000201030 <core::fmt::write>:
  2011e9:       42 ff 54 35 08          call   *0x8(%rbp,%r14,1)
  2011ee:       84 c0                   test   %al,%al

.data.rel.roがあやしい!

https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/specialsections.html

Chromium Docs - Native Relocations

Relative relocations and RELR | MaskRay

$ `brew --prefix binutils`/bin/objcopy -O binary --only-section=.data.rel.ro generated/bin/hello data.bin
[00:34:16]
[local] hikalium@t05.z01.hikalium.com: ~/repo/wasabi  
$ hexdump -C data.bin 
00000000  58 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |X. .............|
00000010  58 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |X. .............|
00000020  58 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |X. .............|
00000030  e0 04 20 00 00 00 00 00  08 00 00 00 00 00 00 00  |. .............|
00000040  08 00 00 00 00 00 00 00  20 06 20 00 00 00 00 00  |........ . .....|
00000050  f0 04 20 00 00 00 00 00  c0 05 20 00 00 00 00 00  |. ...... .....|
00000060  60 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |`. .............|
00000070  60 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |`. .............|
00000080  61 01 20 00 00 00 00 00  08 00 00 00 00 00 00 00  |a. .............|
00000090  60 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |`. .............|
000000a0  e0 04 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000000b0  01 00 00 00 00 00 00 00  70 07 20 00 00 00 00 00  |........p. .....|
000000c0  b0 03 20 00 00 00 00 00  80 04 20 00 00 00 00 00  |. ....... .....|
000000d0  e0 04 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000000e0  01 00 00 00 00 00 00 00  70 19 20 00 00 00 00 00  |........p. .....|
000000f0  94 01 20 00 00 00 00 00  13 00 00 00 00 00 00 00  |.. .............|
00000100  37 00 00 00 05 00 00 00  94 01 20 00 00 00 00 00  |7......... .....|
00000110  13 00 00 00 00 00 00 00  53 00 00 00 05 00 00 00  |........S.......|
00000120  db 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |. .............|
00000130  dc 01 20 00 00 00 00 00  03 00 00 00 00 00 00 00  |. .............|
00000140  a8 02 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
00000150  a8 02 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |. .............|
*
00000170  40 1a 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |@. .............|
00000180  01 00 00 00 00 00 00 00  50 08 20 00 00 00 00 00  |........P. .....|
00000190  b8 02 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000001a0  b8 02 20 00 00 00 00 00  02 00 00 00 00 00 00 00  |. .............|
000001b0

https://uclibc.org/docs/elf-64-gen.pdf

Hardening ELF binaries using Relocation Read-Only (RELRO)

https://maskray.me/blog/2021-08-29-all-about-global-offset-table

Airs – Ian Lance Taylor » Linker relro

cnlelema.github.io

時間切れ

というわけで追いかけていたら.data.rel.roの沼にはまったので今日はここまで。明日もやるかも。おやすみなさい…。

Rustで自作OSをしているときのデバッグ例 - syscall 命令と仲良くなりたい!前編

この記事は自作OS Advent Calendar 2022の17日目の記事です。他の記事も是非お楽しみください!(そして書ける方はぜひ参加してみてください!!)

前回(?)までのあらすじ

hikaliumは自作OS上で動くアプリからsyscall命令を使ってシステムコールを呼べるようにしようと頑張っていたが、なぜか発生するトリプルフォルトによりQEMUが再起動してしまい、3時間のデバッグの末力尽きてしまった。一体なぜ例外が発生するのか、その謎を解くため、我々は数日の休息をとったのち、バイナリの森へと旅立った…。

前回(という名の配信アーカイブ):

www.youtube.com

状況を整理しよう

バイナリの森は危険だ。無闇に動きまわっては、x86の沼に足をとられて命を落としかねない。まずは我々の向かっていた先と、これまでに得た情報をまとめることにしよう。

どこへ向かっていたのか

我々のひとまずの目標は、自作OS上で実行されるアプリケーションから、Rustのprintln!()マクロを介して文字列を出力することであった。Rustはとてもよくできていて、coreクレートの中に、C言語でいうsprintfに相当する、fmtモジュールが存在しており、既存のOSの支援が期待できないno_std環境でも変数の中身などを容易にかつ美しく表示する関数およびマクロを容易に実装することができる。そう、我々はもう闇のようなlibc++やnewlibに頼らなくてもよくなったのだ。10年前のように、自前でsnprintfもどきを実装するまでもない。世界は日増しによくなりつつある。

ではprintln!マクロを実装するにあたり、自作OSが提供しなければならない機能は一体何かというと、それは「文字列を表示する」という機能である。というのも、coreクレートにはOSに依存するような機能は含められないため、「文字を画面に出す」などといった、物理世界とのインタラクション、つまりはハードウエアの制御などのOSが支配している領域の操作はcoreクレートの外で自前で用意してやらなければならない。core::fmtは、フォーマット文字列と引数をもとに、メモリ上に「表示するべき文字列」を生成してはくれるが、それを表示するのは我々の責務なのだ。

というわけで、我々はprint_stringというシステムコールを用意した。これは、メモリ上に存在する文字列へのポインタと、文字列の長さを指定すると、OSがそれを画面上に表示してくれる、というものである。実は、このシステムコール自体はどうも動作しているようで、実際にアプリケーションからprint_stringシステムコールを介して固定の文字列を出力してみた場合は、想定通りに文字列が出力されていたことが確認できている。

それならば、あとはprint_stringに対して、core::fmtによって生成された文字列を渡してあげればよいのではないか、我々はそう踏んでいたのだ。

なにがあったのか

…しかし、その考えはあまりにも浅はかだった。実際に、以下のようにprint_stringを置き換えてみたところ、なんとOSが再起動してしまったのだ。

#![no_std]
#![no_main]

use noli::*;

fn main() -> i64 {
    //sys_print("hello!");
    println!("{}", 42);
    return -42;
}

entry_point!(main);

どうも、何らかの理由でCPUの例外が発生し、トリプルフォルトに至って再起動してしまう、というところまではわかったのだが、我々はそこで一旦力尽きてしまったのだった…。

まずは何が起きているのかを理解しよう

現状は先ほど伝えた通りだ。次は、我々がどちらに向かえばよいのか、それを考えることにしよう。そのためには、我々がいま直面している現象を正しく理解することが必要不可欠だ。まずは、具体的にどのような例外でトリプルフォルトに至っているのか、それを確認することにしよう。

QEMUには、例外が発生した際にログを出力する機能が存在している。これはデフォルトでオフになっているが、-d というフラグを介して制御することが可能だ。

$ qemu-system-x86_64 -d ?
Log items (comma separated):
out_asm         show generated host assembly code for each compiled TB
in_asm          show target assembly code for each compiled TB
op              show micro ops for each compiled TB
op_opt          show micro ops after optimization
op_ind          show micro ops before indirect lowering
int             show interrupts/exceptions in short format
exec            show trace before each executed TB (lots of logs)
cpu             show CPU registers before entering a TB (lots of logs)
fpu             include FPU registers in the 'cpu' logging
mmu             log MMU-related activities
pcall           x86 only: show protected mode far calls/returns/exceptions
cpu_reset       show CPU state before CPU resets
unimp           log unimplemented functionality
guest_errors    log when the guest OS does something invalid (eg accessing a
non-existent register)
page            dump pages at beginning of user mode emulation
nochain         do not chain compiled TBs so that "exec" and "cpu" show
complete traces
plugin          output from TCG plugins

strace          log every user-mode syscall, its input, and its result
tid             open a separate log file per thread; filename must contain '%d'
trace:PATTERN   enable trace events

Use "-d trace:help" to get a list of trace events.

おっと、言い忘れていたが、我々は以下のバージョンのQEMUを自前ビルドして利用している。一応伝えておこう。

$ qemu-system-x86_64 --version
QEMU emulator version 7.2.0 (v7.2.0-dirty)
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

さて、本筋に戻るとしようか。というわけで、以下のようなオプションをQEMUに追加すれば、例外に関するログが、カレントディレクトリの qemu_debug.log というファイルに出力されるはずである。(出力先のファイルは-Dオプションで指定している。) また、 --no-reboot をつけておくと、トリプルフォルトでCPUにリセットがかかった際に、再起動するのではなくQEMUを終了することができるようになる。再起動のループを楽しみたい気分であるなら話は別だが、これも設定しておくとデバッグが楽になるだろう。

-d int,cpu_reset -D qemu_debug.log --no-reboot

さて、これでもう一回自作OSを起動してみる。アプリは自動で起動するように設定してあるから、クラッシュしてすぐQEMUが落ちるはずだ。

…予想通り落ちた。ログを確認しよう。

$ cat qemu_debug.log | grep -A 1 -e check_exception -e Triple
check_exception old: 0xffffffff new 0xe
   159: v=0e e=0000 i=0 cpl=0 IP=0008:0000000000200fd0 pc=0000000000200fd0 SP=0010:000000003e286be0 CR2=0000002000000000
--
check_exception old: 0xffffffff new 0xd
   160: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003e286928 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xffffffff new 0xd
   161: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003e286668 env->regs[R_EAX]=0000002000000000
--
...
--
check_exception old: 0xffffffff new 0xd
  6458: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4c1a8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xffffffff new 0xd
  6459: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xd new 0xe
  6460: v=0e e=0002 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 CR2=000000003de4bed8
--
check_exception old: 0xe new 0xe
  6461: v=08 e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0x8 new 0xe
Triple fault

おお、結構な量のログが出ている。はて、一体何が起きているのだろうか…。

例外コードを順に追っていくと、以下のような流れで例外が起きていることがわかる。

  • ページフォルト(0xe)が0x200fd0の命令を実行中に発生した。ページフォルトの要因となったアドレスは CR2=0x2000000000 である。
  • 一般保護例外(0xd)が0x3e290f91の命令を実行中に発生した。
  • 同じ例外が次々と発生し、スタックポインタが0x3e286928から0x3de4bee8まで変化した。
  • 最終的に、スタックポインタと同じアドレスであるCR2=0x3de4bed8 にアクセスしようとして、再度ページフォルト(0xe)が発生した。
  • ここでさらにページフォルト(0xe)が発生し、これによってダブルフォルト(0x8)が発生した。
  • ダブルフォルト(0x8)の発生中にさらにページフォルト(0xe)が発生し、最終的にトリプルフォルトとなってリセットがかかった。

つまるところ、

  • 最初のきっかけは、0x200fd0の命令がアドレス0x2000000000にアクセスしようとしてページフォルト(0xe)を発生させたことにある。
  • そのページフォルトを処理している最中に一般保護例外(0xd)が発生し、一般保護例外を処理中にもさらに一般保護例外が発生することで、順次スタックが食い潰されていった。
  • 例外ハンドラのスタックが枯渇し、0x3de4bed8にアクセスしようとした段階でページフォルトが発生した。これにより、ダブルフォルトが発生した。
  • ダブルフォルトハンドラも同一のスタックを使用していたため、再度ページフォルトが発生し、トリプルフォルトとなってリセットがかかった。

ということのようだ。 …例外を正しく処理できないOSでごめんなさい…(仕方ないよ、完璧なOSなんて存在しないもの。直していきましょう!)

さて、ここらへんで日付変更線が近づいてきたようだ。続きはまた明日…(睡眠は大事ですよ!おやすみなさい!)

「すいみんpreparation」解説

注意: この文章は教科書ではありませんし正確性も保証されていません。より詳細で正確な情報を知りたい場合には、ここで登場したキーワードをもとに自分で調べてみてください。

はじめに

眠れない夜にOSのことを考えていたら、ふとメロディを思いついてしまったのでそれを打ち込んだのが2/2のことでした。

そこから勢いで曲を打ち込んで歌詞をつくり(この段階で歌詞は出来上がっていたので伏線)

歌って動画を作った結果がこちらです。

なお歌はあまり上手くないので、声なしのバージョンがいいよという方はSoundCloudにありますのでそちらもどうぞ!

soundcloud.com

さて、この曲に対する人々の反応ですが、

はい、極めて正常な反応だと思います。

実のところ、この歌詞にはOSネタを大量に盛り込んでいたので、おそらくそのあたりの知識がないと完全には面白さが伝わらなかった可能性があります。

そこで本記事では、「すいみんpreparation」の歌詞を追いつつ、どのようにOSネタとの関連があるのか、どのような意図をこめた歌詞になっているのかについて解説したいと思います。

全体のテーマ

動画の概要欄に「やりたいことで手一杯なOSのみなさんのために」と書かれている通り、OSの果たす役割のうちの一つである「資源の管理」という側面について、その難しさを日常生活になぞらえて描写することが今回のテーマでした。

特に、タスクのスケジューリング問題に関しては、かなり強い仮定をおいてもなおNP完全な問題であることが "NP-complete scheduling problems" (J.D. Ullman, 1975) で示されています。ちなみにUllman氏は、コンパイラを実装したことのある皆様にはおなじみ、通称ドラゴンブックの作者の一人です。

www.sciencedirect.com

OSのみなさんだけでなく、人間のみなさんにとっても、日々遂行しなければならないタスクが山積していて、その処理に追われているという状況はよくみられることでしょう。

そのような困難な状況下にあっても、最優先で処理すべきタスクのひとつ、それが睡眠である、というのが本作品の主要な論点になります。

歌詞とその背景

では、最初から順番に歌詞を追ってゆきましょう。

いつも気づいたら 時計の針はとびまわり 
明日という日が 足音立てず向かってくるよ

作業に没頭していると、時間があっという間に過ぎてしまって、気づけば夜中になっていた!という経験はみなさんよくあるのではないでしょうか。

OSにおいては、並行処理における競合状態を回避するために「割り込み」を禁止することがあります。(このような競合状態の発生する可能性のある処理の範囲のことを、「クリティカルセクション」と呼びます。)

「割り込み」は、外部デバイスからの信号などを契機として、現在実行している処理に優先してなんらかの処理を行わせる仕組みのこと、またそれを引き起こす信号のことを指します。

割り込みを発生させる外部デバイスの状態変化の例としては、キーボードのキーを押下した時や、ネットワークを介してパケットが到着した時、外部記憶装置に対する書き込み要求が完了した時などが挙げられます。

他にも、重要かつ基本的な割り込みの一つとして、タイマー割り込みというものが存在します。これは、ある時間(たとえば10ms)が経過するごとに割り込みを発生させる、というものです。

OSは、このタイマー割り込みを活用することにより、時分割マルチタスクを実現しています。マルチタスクは、CPUの処理能力という限られた資源を有効活用するというOSの大事な役割のひとつです。 したがって、それを支えるタイマーや時刻の管理も、実はOSの重要な仕事のひとつなのです。(Linuxではtimekeepingサブシステムがこれを担当しています。)

さて、以上のことを念頭に一つ考えてみることにしましょう。もしも割り込みをずっと止めていたら、つまり割り込みを無視し続けたら、一体何が起こるでしょうか?

……そうです、時分割マルチタスクの仕組みが正しく動作せず、現在実行しているタスクが終わるまでずっとそのタスクを処理しつづけることになります。

これは、何かに没頭して時間の経過を忘れてしまうことと対応します。(人間においてこのような状況は「過集中」と呼ばれることもあります。)

その結果として、いつの間にか意図せず夜ふかしをしてしまうことを「明日という日が 足音立てず向かってくるよ」と表現しています。

また、タイマー割り込みによって定期的に行われていた時刻の管理も正しく行われなくなるため、時計が狂ってしまうこともあります。

それが「いつも気づいたら 時計の針はとびまわり」という部分の意味です。

今日も 下手したら 同じところを周ってばかりで
明日できるさ 帰納法によれば成り立たぬ

得てして長時間の割り込み禁止が発生してしまうのはバグの存在を意味します。というのも、先ほど説明した通り、長時間の割り込み禁止は様々な問題を引き起こす可能性があるため、そのような状況が発生しないように気をつけてコードを書く責任が開発者にはあるからです。

そもそも、アプリケーションレベルでは、割り込みを禁止することは一般に許可されていません。OSのみが割り込みに関連する制御を行うことができるよう、CPUの保護機構を活用して制限をかけている場合がほとんどです。(ユーザーモードカーネルモード・特権命令 等々で調べてみてください。)

したがって、この種のバグは、OS側で処理が無限ループするなどの要因で発生していることが考えられます。これを描いたのが「今日も 下手したら 同じところを周ってばかりで」という部分です。

これを日常生活における類推に落とし込むと「明日から本気出す」などの、問題を先送りにする場面が想像できます。(耳が痛いです…。)

残念ながら「明日になればできる」と考えているときは、永遠に来ない相対的な「明日」に対して問題の先送りを繰り返し、目標が達成されないか、達成されたとしても非常に長い時間がかかることが経験上知られています。

このように、いくつかの事実から一般的な規則を導く手法を枚挙的帰納法といいます。ただ、枚挙的帰納法数学的帰納法とは異なり、導かれた規則が正しいとは限りません。(ということで希望を失わずにがんばりましょう!)

やりたいことばかりで リソース不足まっしぐらなの
コンテキストスイッチだけで 手一杯だよ

これ…ほんとこれなんですよ。

時間は有限な資源です。それはOSにおいても同様です。 CPUのコア一つ一つは、単純な操作を繰り返しているに過ぎず、同時に複数の処理を行うことができません。 そのため、並行して処理を進めるためには、何らかの工夫をする必要があります。 複数のコアを利用して処理を進める並列計算はひとつの手段ですが、それでもコア数分しか並行に演算を行うことができません。 そこで、ほとんどのオペレーティングシステムでは、あるコアの上で実行されるアプリケーションを短い時間間隔(これをタイムスライスといいます)で切り替えることにより、大局的にみれば複数のアプリケーションが並行して動作しているようにみせる機能を有しています。これを、マルチタスキングといいます。

あるCPU上で実行されているアプリケーションを別のアプリケーションに切り替える際には、現在のCPUの内部状態の一部(CPUコンテキスト)を保存してあげる必要があります。この作業をコンテキストスイッチといいます。 より具体的には、CPUのレジスタに入っている値を、各プロセスごとに別々に用意されたメモリ領域にコピーして退避するなどの処理を行なっています。

コンテキストスイッチもCPU上で実行されるプログラムにすぎませんから、その処理には一定の時間がかかります。

ここまでくれば、もう察しの良い方は気づいているかもしれません…そうです、もしも非常に多くのアプリケーションを単一のマシン上で実行しようとすると、コンテキストスイッチにかかる時間が非常に大きくなり、さらには各アプリケーションが実行される頻度もどんどん下がってゆくことになります。つまり、アプリケーションの処理がほとんど進まなくなってしまう可能性があるわけです。これを飢餓状態(resource starvation)といいます。

…ですから、やりたいこと(アプリケーション)の数が多すぎるとリソース(資源、ここではCPU時間)不足に陥り、コンテキストスイッチだけでCPU時間を食い尽くす飢餓状態になる、ということです。

みなさんもやりたいことスタックの大きさには気をつけておきましょう。(耳が痛いです…。)

ぐーるぐるぐる回る run queue
このスケジューラーは壊れてる
panic 寸前のシステムを
救えるのは君だけさ ほらね

さて、前項で説明したコンテキストスイッチを司るのは、OSの中でもスケジューラーと呼ばれる部分になります。スケジューラーは、実行可能だが現在CPU上では実行されていないタスクの一覧をrun queueとよばれるデータ構造に保持しており、その中から次に実行すべきタスクを選択して、そのプロセスにコンテキストスイッチを行うことで、タスクを順々に実行していきます。(ちなみにここではアプリケーション・プロセス・・タスクという用語が入り乱れていますが、これらの厳密な定義はOSの実装に依存しますので、まあとにかく「処理の管理単位」なんだなあ、と思っていただければ助かります。)

実を言うと近年のほとんどのOSでは、タスクごとの優先度やリアルタイムタスクの実行などを考慮するために、単純なqueueを用いてスケジューラーが実装されているわけではないのですが、シンプルなモデルとしてここではqueueで考えることにします。

queueの中には実行を待っているタスクが入っており、OSはそれらを順番に取り出してCPUに割り当ててゆくことでマルチタスクを実現するので、まるでタスクがキューの中をぐるぐる回っているかのような感じになります。

もしもスケジューラーにバグがあったら、あるプログラムが永遠に実行されなかったり、色々困ったことが起きるかもしれません。下手をすると、システムの動作に必要なプロセスまでもがいつまでも実行されず、OSが固まってしまう、ということもあるかもしれません。そういった、OSにとって重大な問題が発生して、回復できる見込みがない場合は、OSはエラーメッセージを出力して再起動します。これをpanicと呼ぶことがあります。(Windowsでいえばブルースクリーンのようなものです。)

panicが発生するようなギリギリの状態にOSがある際は、十中八九割り込みが禁止されています。このような状況下では、キーボード割り込みなどの通常の割り込みは受け付けられません。

でも、こんなときでもなんとかなる割り込みが実はあるのです。

すいみん睡眠preparation 最高優先度でよろしくね
すいみん睡眠preparation 問答無用で割り込むよ
すいみん睡眠preparation これは無視不可能なシグナル
すいみん睡眠preparation いますぐおふとんへIRET

x86には、NMIという割り込みがあります。これは、Non-Maskable Interruptの略で、CPUでたとえ割り込みを禁止していたとしても、この割り込みに関しては即座に処理が行われる、という特別な割り込みです。(実はAMDのハイパーバイザまわりの文脈ではNMIをマスクできるとかいうNMIのアイデンディティ崩壊級の面白い話があったりするのですがそこらへんは一旦忘れましょう。)当然、この割り込みは、あらゆる割り込みに優先して(他の割り込み処理を実行中でも関係なく)処理が行われます。つまり、無視できないシグナルなわけです。

みなさんが何かの作業に熱中し過ぎている時には、きっと割り込みは無効化されているのでしょう。たとえば、宅配便が届いてインターホンが鳴っても気づかなかったり、呼びかけられても気づけなかったりするのがその一例です。

しかし、時には何よりも優先して実行すべきことがあるケースがあります。それが睡眠です(要出典)。というかそうしたいですね。難しいですが。

言い換えれば、我々の生活の健全性を担保するためには「マスク不可能な、おふとんへ今すぐ行け割り込み」が必要なわけです。

で、IRETというのは、RETurn from Interrtuptという命令で、割り込み処理を終了し、割り込みが起こった際に実行されていたコードの実行を再開するという命令です。 この命令はコンテキストスイッチでも利用されているので、まあ「マスク不可能な、おふとんへ今すぐ行け割り込み」が発生したら、熱中していたタスクから一旦コンテキストスイッチして、睡眠プロセスへ切り替えるというのが重要ですよ、という話をここでしています。

たまにどうしても 無理なときは
仕方ないから 全部吐き出して
今度直せばいいのさ 
次はもっとうまくやれるはず

まあ、無事におふとんへ辿り着けたらいいわけですが、ときには睡眠失敗するケースもあります。そういうときはまあpanicするしかないわけですが、panicする際は、後でなぜpanicしたのかデバッグできるように、メモリの状態をダンプすることがあります。これをコアダンプと言います。また、コアダンプを「吐く」とか「吐かせる」という慣用表現があり(dumpなので)、つまるところどうしても眠れないなら、現在のメモリの状態(頭の中のもやもや)を全部紙かなんかに書き出して、次の修正の参考にするといいよ、ってことを言ってます。

プログラムに完璧はありません。常にバグがあります。なので、コアダンプは大事です。失敗しないことよりも、次の失敗を防ぐ方法をつくること、そしてそのデバッグを容易にする仕組みを用意することが重要です。

だから
すいみん睡眠preparation 入出力は一時停止で
すいみん睡眠preparation 机の上を片付けようね
すいみん睡眠preparation 頭の中身書き残したら
すいみん睡眠preparation おやすみ世界

こっちは正常系の話で、suspend to diskの処理を表現しています。 よく、コンピューターにおけるメモリは、作業をするための机として例えられます。机が広ければよりたくさんのことを同時に効率よくできる、というアナロジーから来ているのでしょう。 さて、コンピューターがsuspendする際には、電力消費を抑えるために、メインメモリの内容をディスクなどの不揮発性の記憶装置に書き出します。(メモリが不揮発性とは限らないよ!NVDIMMがあるよ!と叫んでいる私のことは無視してください。)というのも、通常のコンピューターのメインメモリとして利用されているDRAMは、電荷コンデンサにためるような感じでデータを保持しており、その電荷は時間経過で抜けてしまうため、定期的にデータを読み出して再度書き込むという操作(リフレッシュ)を行う必要があり、継続的に電力を消費するため、ここにデータを置いたままにしておくのは不都合だからです。

とはいえ、OSのほとんどの状態はメモリ上にありますから、OSが必要な処理、たとえば入出力装置とのやりとりなどがsuspend処理中に起こるとよろしくありません。というわけで、一般的にコンピューターをsuspendする場合には、アプリケーションの動作を停止し、さらに入出力装置を一旦止め、そしてメモリの内容をディスクに書き出し、電源を落とす、という処理になります。

みなさんが睡眠の準備をする際も、うっかりSNSなどを見て割り込み処理が発生してしまうと、睡眠失敗panic路線まっしぐらになる可能性が高いです。というわけで、この順序を守って、安全にシステムをsuspendするようにしましょう。

ということで、おやすみなさい!よい夢を!

光造形3Dプリンターはじめてみた(ELEGOO Mars 2 Pro)

きっかけ

この前の記事で、私もついに自作キーボードに足を踏み入れたわけですが、keyball46の個人的につらいポイントとして、キーがとても少ないというのが挙げられます。 というのも、私はこれまでErgoDox EZを使用していたため、キーの数が十二分にあるような環境に慣れすぎており、さすがにキーの数が76から46に激減するとかなりの厳しさを感じます。(もちろん慣れの問題だとは思いますが。)

せめて、親指あたりにもう数キーくらい欲しいところです。

さて、実はkeyball46をよく見てみると、キーマトリクス的には左右それぞれ6*4=24キーの合計48キーに対応できるはずなのに、左右共に1キーずつ不足していることに気づくはずです。

f:id:hikalium:20220105211520p:plain
実はそれぞれもう1キーずつサポートできる余裕がありそう

この不足しているキーに対応する接点は、keyball46のオリジナルのファームウエアでは、キーボードの左右を自動で検出するためのジャンパ代わりに利用されています。

f:id:hikalium:20220105211737j:plain
SWと書かれている接点(中央左下)が、欠けているキーにあたる

ですが、私は自前でファームウエアを書いたので、これらの接点はただ「どこにもつながっていない幻のキー」になっているだけなのです。かなしいですね。

…ということは、ここにスイッチを生やしたらもう1キーずつ手に入れられるのでは?

そう思った私は、3Dプリンタを買うことにしました(論理の飛躍が激しい)

買ったもの

数時間ネットの海をさまよった結果、ELEGOO MARS PRO 2という光造形方式の3DプリンターAmazonから34999円で購入しました。

また、光造形3Dプリンターは、紫外線を当てると液体から固体に変化するUVレジンというものを使用して物体を出力します。これには通常のレジンと水洗いレジンの2種類が大きく存在し、通常のレジンは印刷後にアルコール系の溶剤で余剰レジンを洗浄する必要があるのですが、水洗いレジンだと水で同じことができるため便利らしいと聞き、SK水洗いレジンという商品を購入しました。これはAmazonで1000mlが6980円]でした。

これだけあれば印刷自体はできるようになりますが、色々作業をするときに下に敷くバットだったり、洗浄時に使うタッパーだったり、掃除につかうペーパータオルとかアルコールティッシュとかも買っておくといいみたいです。他にも、二次硬化という、印刷後の物体を乾燥させた後に、より十分に固化させるために紫外線を当てる手順も場合によっては行われるので、そのための器具が色々販売されていますが、最悪太陽光に当てれば紫外線はタダで手に入りますし、必要になったらそのときに購入すればいいかなと思って、ひとまず私は最初の時点では購入しませんでした。

さらに、マスクやゴム手袋・ビニール手袋などの保護具もそろえておく必要があります。(上記の水洗いレジンはそこまで匂いはきつくないですが、それでもある程度は吸ったり触れたりすると有害なはずですので、忘れずに準備しましょう。)

あとは、使用後のレジンを濾過するための、ろうととか濾紙とか、濾過したレジンを入れるふたつきの容器とかあるといいかもしれないです。(私は一通りやってから必要だと学んだのでポチりました。)

開封からはじめての印刷まで

とりあえず、適当な場所に開封して設置してみた図がこれです。

f:id:hikalium:20220105213719j:plain
設置完了した図

組み立てる必要のある部分はほんの僅かで、基本的にはレジンを入れるタンクを紫外線を発するスクリーンの上にのせて、印刷物をくっつけて引っ張るプラットフォームを取り付けてあげるだけです。

ただ、この「プラットフォームを取り付ける」というステップが結構重要で、しかも取扱説明書の方法だとうまくやるのが難しいです。

ネットで調べたところ、手順としては

  1. レジンタンクをスクリーンの上におく
  2. プラットフォームの角度調整ネジ(六角レンチで回す2ヶ所のやつ)をゆるめる(手で回す固定ネジ自体はこの段階できっちり締めておく)
  3. プラットフォームをホームポジションに移動するボタンを押して下げる(大まかな向きはこの段階で合わせておく)
  4. プラットフォームの上下を微調整して、トレイの底≒スクリーンに密着するようにする
  5. 角度調整ネジを動かない程度にそっと締めて、Z=0をこの位置で設定する
  6. 少しプラットフォームを上げてから、ネジを本締めする(ネジを締める力でスクリーンが割れないようにするために浮かせている)

みたいな感じで調節するといいみたいです。(要するに、プラットフォームがZ=0にあるときに、スクリーンに接触している必要があるわけです。)

そんなわけで、付属しているUSBメモリをプリンタに挿して、ファイルを選択し(なんと説明書に書いてある拡張子とは違うやつを選ぶ必要があった…モデルのプレビューが表示されるファイルが印刷できるやつです(.ctbファイル))、あとは待っていると…

f:id:hikalium:20220105214914j:plain
印刷途中のようす(液体から固体ができあがってくるの、見てて楽しい)

こんな感じで生えてきます。(うまくいっていると、各層の露光が終わってプラットフォームが上がる時に、軽く「ペリペリッ」って音がします。これは、レジンタンクの底から、固化したレジンが剥がれて、プラットフォームの側についている証拠なので、これが聞こえたら成功の望みが高めな気がします。(必要条件でも十分条件でもないですが。))

さらに部屋を片付けつつ2時間37分くらい待つと、こんなかんじで出来上がります。

f:id:hikalium:20220105215154j:plain
2h37mで完成!(想像より早かった)

出来上がった物体はプラットフォームにくっついているので、これを同封されていたヘラで剥がします。

最初、力加減がわからなくて戦々恐々としていたのですが、プラットフォームは金属でできているので、プラットフォームの面を垂直にしてバットなどの上におき、同封されていた金属製のヘラをその面に添わせて印刷物の底を「えいやっ!」って何回か叩くと取れました。(ヘラとプラットフォームが削れてできた汚れが印刷物に若干ついてしまったが、まあ許容範囲でしょう。印刷物は印刷完了直後の段階ですでに結構硬いので、そこまで手加減しなくても大丈夫みたいです。)

f:id:hikalium:20220105215526j:plain
印刷物をプラットフォームから取り外した様子(最初はけっこう難儀した)

あとは適当な大きめの容器に水をいれて、そこに印刷物をいれて余剰レジンを洗い流し、ペーパータオルで拭けば完成です。わーい!

あとかたづけ系のTips

印刷後にどう片付ければ/次回の印刷の準備をすればいいのか、いまいち体系的な解説がなかったので、色々調べた結果私がやったことを書いておきます。

  • プラットフォームに残っている固化したレジンは金属のヘラで綺麗に除去し、アルコールティッシュなどで汚れをとる
    • 固形物が残っていると、プラットフォームが十分に下がらずスクリーンに密着しないので、最初の層がうまく生成されない原因になる
  • 印刷に失敗した場合は、固化したレジンがレジンタンクの底のフィルム(つまりスクリーン側)に付着しているので、レジンタンクの底のフィルムやディスプレイを破壊しないように手やプラスチックのヘラでそっと除去する
    • プリンターについているクリーニング機能を使うと楽。
      • これは、印刷面全体に紫外線を短時間(デフォルト15秒)照射することで、レジンタンクの底にきれいに1層固体化したレジンを生成してくれるので、それを剥がせばOKという機能。
      • 底に生成されたレジンの層をはがすために、適当にまあまあ丈夫な紙の切れ端とかを先にレジンタンクの底に触れるように入れてから、クリーニング機能の照射を行い、紙切れをひっぱりだすと、ペリペリと剥がれてくれる(たのしい)
    • 他の方法としては、固化したレジンは1層だけでも意外と硬いので、レジンタンクを外して下側から指で押してフィルムを少したわませてあげると、ぺりっと剥がれてくれる。
      • ただし、このときレジンタンクの外側にレジンがつかないように気をつけたほうがよい。そうしないと、レジンがスクリーンに付着して、それが次の印刷時に固化して取れなくなってしまう。そうなったらthe endなので、再度レジンタンクを設置する前にアルコールティッシュなどできれいにふいておいたほうがよさそう。
  • 印刷に失敗したりして、固化したレジンが混ざってしまったりしたときや、しばらく(1週間くらい?)印刷しないときは、一旦中身を全部外に出して、ろうとで濾過する。
    • 紫外線に当てなければ固化しないので、少しの間使わないくらいならそのまま放置でも大丈夫そう?(未検証)
  • 洗浄に使った水は、太陽光に当てるなどしてレジンを固化してから、固形物を濾過して除去し、残りの液は下水に流せばよいみたいです。固形物は燃えないゴミみたい。(レジンの取扱説明をよく読みましょう。)

3Dデータ作成Tips

適当にBlenderでモデルをつくってexportする際にいくつかハマりポイントがあったのでそれについてもメモ。

  • 最初にScene propertyのUnit scaleを0.001に設定しておく。こうしておかないと、Blender上と実際の印刷時の縮尺がずれる。逆に言えば、これを設定しておけばBlender内でcmとかの単位でモデリングすれば、それがそのまま出力される。

f:id:hikalium:20220105222505p:plain
BlenderのScene propertyの設定

  • エクスポート時はSTL形式を選ぶ。スケールはさっき設定したので、デフォルトの1.0で大丈夫。他のプロパティもデフォルトでOK。Apply ModifiersをOnにしておけば、ちゃんとModifierが適用された後のモデルが出力されるので、Blender上でModifierをApplyしておかなくても大丈夫なので便利。Selection Onlyにチェックを入れれば、選択したObjectだけを書き出せるので便利。

  • Blender上でのマイナスZ方向が、印刷時にプラットフォームに接する側になる。(もちろん、CHITUBOXでも回転はできるので後で修正もできるが、最初から合わせておくと楽。)

  • サポートをつけて印刷するの、むずかしい。接地面が小さいobjectを印刷するのは難しい。

  • でも上の図みたいな平面は割と簡単に出せる。Z軸方向に薄いオブジェクトだと印刷もはやい。

f:id:hikalium:20220105223357j:plain
適当にBlenderでつくったモデル(test.blend)の出力結果

  • 印刷物をプラットフォームから剥がしやすいように、モデルの接地面側に隙間をつくっておくと楽かも?(test.blendでは角をひとつ隙間があくようにカットしたところ、かなり楽に外せたのでよかった。)

3Dデータをスライスして3Dプリント可能にするCHITUBOXというソフトの注意点

  • プリンタのプロファイル設定はちゃんとしましょう(最初忘れててよくわからなくなった)
    • ファイル形式もどうやらプロファイル設定に依存して自動で変わるみたい
    • とりあえずデフォルト設定でふつうに簡単なモデル(上に示したtest.blendのようなもの)なら印刷できた
  • 接地面が小さいと難易度が上がるのでがんばりましょう(サポートを生やすなどの工夫が必要みたいだが私の鍛錬が足りないのでまだ印刷には成功していない)

生成物のクオリティについて

  • かなりクオリティは高い。積層痕が全然見えないレベル。サンプルデータのチェスのコマの天面に印刷されている、2mmくらいの大きさの文字もばっちり見えている。
  • 使用するレジンにもよるとは思うが、見た目や感触は完全に切り餅みたいな感じ。だが切り餅よりも硬い。(年末年始なのでなおそう見える(なお私は今季まだ一度も餅を食べていない…。))
  • 出力された物体はかなり硬い。厚さ6mmもあれば全くしならない。
  • 精度もかなり高い。test.blendにはキースイッチがはまる穴をあけたが、ふつうに綺麗にはまって衝撃を受けた。(乾燥や時間が経つにつれて収縮はあるかもしれないが、未確認。)
    • ただし、最初の層は他の層にくらべて硬く、さらに多少XY平面方向に太る。各方向に0.1~0.2mmくらい。これは初期層は他の層にくらべて露光時間が長く設定されているためである。

f:id:hikalium:20220105225053j:plain
キースイッチをはめてみた図。引っ張るとキーキャップの方が先にとれるレベルでしっかりはまっている。

まとめ

もっと早く買えばよかった。みんなも3Dプリンタを買ってあそぼう!

2021年の振り返り

今年は何があったかを振り返ってまとめておく。

1月

やっとNoogler Hat (物理)が届いた

2月

Writing an OS in Rust 輪読会をやっていた(Pagingの回を担当した)

3月

hikalium(物理)をPixivFACTORYで発注したのが届いた

あと前の年にもらっていた賞の賞状が届いた

4月

nekoi7yuさんと一緒に低レイヤ雑談する配信をした

www.youtube.com

あと私のL2キャッシュ(本棚)が低レイヤすぎて、みんな結構面白がってくれた

そしてKOBA789氏と共に、自作RDBMSを自作OSに移植するコラボ配信をした

ダイジェスト版も789氏が作ってくれた

5月

サイボウズ・ラボユース10周年記念の対談記事が公開された。

gihyo.jp

6月

KAT WALK Cが届いた(これでVR内で走れる!)

とてもでかいです。

あとLinuxの動くRISC-Vマシンで少し遊んだ(もっと遊んであげなきゃ…)

あと人々にmallocを実装させるなどしていた

7月

hikaliumのファンアートが初めて作成された(うれしかった!)

そしてさらに散財機材がアップデートされた

あとはRasPiと低いレイヤでおはなしして仲良くなった

hikalium.hatenablog.jp

8月

kurenaifさんにPOODLE Attackを教わった(させていただいた)

FLAGがとれたときの様子

この時期の作業環境はこんな感じだった

あと2回目のセキュリティアップデートをインストールした際のログがこちら

そしてセキュキャンの時期が近づく…(Tシャツのデザイン決めにほんの少しだけ関わった)

背中の暗号はみなさん解けましたか?

そしてお仕事で最近やっていたことについて話した。低レイヤな仕事、一緒にやりませんか!

www.youtube.com

スライドはここから辿れます。

あと、WebMIDI経由でnanoKONTROL2と戯れたりもしていた。

9月

hikaliumロゴアクリルキーホルダーの試作品が届いた

なんとここから買えます(この記事執筆現在、8個くらい売れていて驚きました)

セキュキャンのOS自作ゼミでポインタについて熱く語るなどの社会貢献をおこなった(?)

そしてついに低レイヤーガールチャンネルの収益化が承認された!(ありがとうございます!)

あとInfraStudyという勉強会で、人々を低レイヤーへ誘い込むオタク早口トークをした

www.youtube.com

10月

語り得ぬものについては沈黙せねばならない。

童心に返ってスライムをmake (smileではない)

がんばったので昇進して「(ふつうの)Software Engineer」になりました。(入社から約1年半でなので、まあまあ早いほう。)

お給料結構上がってびっくりした。

C言語はおしまいです(?)

JINS MEMEというIoTメガネで遊び始めた

そしてついに、ついに、1年半のおあずけを経て、健康を享受しはじめた!

ハロウィーンも低レイヤでした

11月

789氏のJINS MEME hackの結果を再利用させてもらって、hikaliumの頭をぐるぐるさせるなどした

12月

そしてキーボードが増えた

PiyoConfというイベントでライブLinuxカーネルいじりをしてみた

www.youtube.com

unameシステムコールを無理やり改造することでLinuxがPiyoxになったのでした

その余波でスパチャの嵐を受けた…(ありがとうございます!)

ラテアートを作ったり

OS自作の歌を作って歌ったり(生声注意)

www.youtube.com

キーボードを作るはずが、買うパーツを間違えたのでRustでファームウエアを書いたりしていた。詳細な顛末は別記事にて。

hikalium.hatenablog.jp

通年

  • 低レイヤーガール - YouTube
    • d0iasm氏と一緒に、自作ブラウザとか自作OSをする配信を土曜日に不定期開催している
    • 12/25に、おすすめのコンピューター関連書籍について語り合う配信をしたので、もし興味があればご覧ください!

youtu.be

  • Writing an OS in Rust 輪読会
    • Writing an OS in Rustという記事をみんなで交代で読んでゆく会。ひとまず掲載されている分の記事はすべてやってアーカイブが残っているので、興味のある方はぜひ。再生リストはこちら

www.youtube.com

  • Rust Raspberry Pi OS Tutorials 輪読会

  • 自作OSもくもく会

    • osdev-jpとして、uchan_nos氏と一緒に開催している。今年は5回開催された。隔月開催が目標だったので、まあまあといえよう。 発表の録画も発表者の許可があるものについては公開している。(まだアップロードしてないものがあるかも。)

www.youtube.com

まとめ

意外と色々やってたんだなあ。えらい!

(毎年思っていることだけど)来年はゆっくり過ごしたいと思います…。

もっと気楽に生きたいですね。なかなか難しいですが。

hikaliumと関わりのあった皆様、今年も大変お世話になりました。来年も、よいお年をお過ごしください!