80
Solaris (Branded) Zone Internals for Tokyo OpenSolaris 勉強会 by 藤原 克則

Solaris (Branded) Zone Internals

Embed Size (px)

Citation preview

Page 1: Solaris (Branded) Zone Internals

Solaris (Branded) Zone Internals

for Tokyo OpenSolaris 勉強会

by 藤原 克則

Page 2: Solaris (Branded) Zone Internals

自己紹介

• 藤原 克則 (FUJIWARA Katsunori)

• ホームページ http://www.lares.dti.ne.jp/~foozy/index.ja.html

• ブログ http://d.hatena.ne.jp/flying-foozy/

• Twitter @flyingfoozy

Page 3: Solaris (Branded) Zone Internals

はじめに

Page 4: Solaris (Branded) Zone Internals

事の起こり

• 丁度ブランドゾーン周りのカーネルコードを眺めていた時期に、コンテナ仮想化/Solaris ゾーン周りで幾つか見逃せない情報が……

Page 5: Solaris (Branded) Zone Internals

情報 – その1

• “Windows 10のAnniversary UpdateによってWindows 10システム上でLinuxのバイナリー

をシームレスに実行することができるようになると、Microsoftは本日のBuildで発表” (https://www.infoq.com/jp/news/2016/04/linux-windows-together)

• それ、なんて lx brand zone ????

Page 6: Solaris (Branded) Zone Internals

情報 – その2

• “The dream is alive! Running Linux containers on an illumos kernel” by Bryan Cantrill 曰く:

– “system calls bounced back to a user-level emulation library” (http://www.slideshare.net/bcantrill/illumos-lx)

• “user-level emulation” とは何ぞや????

Page 7: Solaris (Branded) Zone Internals

この際だから徹底的に!

• 良い機会なので、ブランドゾーンに関して、実現方式等を調べた結果をまとめてみました

• 便宜上、「ブランドゾーン」の説明に先立って、「ゾーン」自身についても説明します

• 但し、いつものごとく、「カーネル目線」での説明になります(笑)

Page 8: Solaris (Branded) Zone Internals

関数名等の表記

• “U:” は「ユーザ空間での実行」、 または「ユーザ空間向けの定義」の意味

• “D:” は「デーモン側での実行」の意味 (※ 実行主体が複数ある場合に使用)

• “K:” は「カーネル空間での実行」の意味

• 接頭辞がないものは

–カーネル/ユーザで共通の定義か、

–紛らわしさがない

Page 9: Solaris (Branded) Zone Internals

door! door! door!

• 実は zoneadm は Client/Server モデル!

• フロントエンドの zoneadm と、 バックエンドの zoneadmd が連携して処理

• zoneadmd は各ゾーン毎に1プロセス

• zoneadm と zoneadmd は door IPC で通信

• door IPC について話し出すと長くなるので、 この資料では door IPC の話は封印(笑)

Page 10: Solaris (Branded) Zone Internals

ゾーンの状態遷移

Page 11: Solaris (Branded) Zone Internals

UNINITIALIZED INITIALIZED READY

BOOTING

RUNNING

SHUTTING_DOWN EMPTY DOWN

DYING

DEAD

K:zone_create()

K:zone_shutdown()

K:zo

ne_d

estroy()

K:zo

ne_b

oo

t() Zone Scheduler (zsched)

Page 12: Solaris (Branded) Zone Internals

ゾーンの初期化

1. “zoneadm ready” コマンド実行

2. zoneadmd への Z_READY 要求

3. D:zone_create() 実行

4. zone(ZONE_CREATE) システムコールを発行

5. K:zone(ZONE_CREATE) 実行

6. K:zone_create() 実行

Page 13: Solaris (Branded) Zone Internals

K:zone_create()

K:zsched()

UNINITIALIZED INITIALIZED READY

wait

wait

Page 14: Solaris (Branded) Zone Internals

ゾーンの起動

1. “zoneadm boot” コマンド実行

2. zoneadmd への Z_BOOT 要求

3. D:zone_boot() 実行

4. zone(ZONE_BOOT) システムコールを発行

5. K:zone(ZONE_BOOT) 実行

6. K:zone_boot() 実行

Page 15: Solaris (Branded) Zone Internals

READY

BOOTING

RUNNING

K:zone_boot() K:zsched()

wait

wait

change by K:zone_set_status()

notify

notify

wait

spawn by K:newproc()

K:zone_start_init()

change by K:zone_set_status()

Page 16: Solaris (Branded) Zone Internals

ゾーンの停止

1. “zoneadm halt” コマンド実行

2. zoneadmd への Z_HALT 要求

3. D:zone_halt() 実行

4. D:zone_shutdown() 実行

5. zone(ZONE_SHUTDOWN) システムコールを発行

6. K:zone(ZONE_SHUTDOWN) 実行

7. K:zone_shutdown() 実行

Page 17: Solaris (Branded) Zone Internals

RUNNING

SHUTTING_DOWN EMPTY DOWN

K:zone_shutdown()

K:zsched() wait

wait

notify

K:zone_task_rele() for the last user task K:zthread_exit()

for the last kernel thread

Page 18: Solaris (Branded) Zone Internals

ゾーンの破棄

1. “zoneadm halt” コマンド実行

2. zoneadmd への Z_HALT 要求

3. D:zone_halt() 実行

4. D:zone_destroy () 実行

5. zone(ZONE_DESTROY) システムコールを発行

6. K:zone(ZONE_DESTROY) 実行

7. K:zone_destroy() 実行

Page 19: Solaris (Branded) Zone Internals

DOWN

DYING

DEAD

K:zone_destroy()

zsched()

change by K:zone_set_status()

wait

notify

notify

wait

terminate

change by K:zone_set_status() TODO:

what code

path ?

Page 20: Solaris (Branded) Zone Internals

ゾーン再起動時の挙動

Page 21: Solaris (Branded) Zone Internals

zone_t の再利用

• zone_t は「再起動中」等の状態を持たない

• DEAD 状態になった zone_t は、一旦「未使用」扱いになる

• 同一のゾーンを再度起動させた場合でも、UNINITIALIZED 状態からの遷移のやり直しに過ぎない

Page 22: Solaris (Branded) Zone Internals

グローバルゾーンからの再起動

• zoneadm reboot は以下の逐次実行と等価

– zoneadm halt

– zoneadm ready

– zoneadm bootup

• zoneadm reboot での Z_REBOOT 要求契機で zoneadm”d” 側で halt/ready/bootup を実施

Page 23: Solaris (Branded) Zone Internals

ゾーン内部からの再起動(1)

1. ゾーン内部での "/sbin/uadmin 1" 又は "halt" コマンド実行

2. uadmin(A_SHUTDOWN) システムコール発行

3. K:uadmin(A_SHUTDOWN) 実行

4. K:zone_kadmin(A_SHUTDOWN) 実行

5. K:zone_ki_call_zoneadmd() でスレッド作成

– Z_REBOOT 引数指定

Page 24: Solaris (Branded) Zone Internals

ゾーン内部からの再起動(2)

1. K:zone_ki_call_zoneadmd() で以下を実施

1. K:zone_empty() でゾーン内の全タスクを終了 (内部的に K:killall() 使用)

2. K:door_ki_upcall_limited() で zoneadmd への Z_REBOOT 要求

3. 再起動が完了するか、エラー中断されるまで、Z_REBOOT 要求の繰り返し

2. zoneadmd 側再起動手順は、グローバルゾーンからの再起動と同じ

Page 25: Solaris (Branded) Zone Internals

状態定義の差異

Page 26: Solaris (Branded) Zone Internals

UNINITIALIZED INITIALIZED READY

BOOTING

RUNNING

SHUTTING_DOWN EMPTY DOWN

DYING

DEAD

Page 27: Solaris (Branded) Zone Internals

UNINITIALIZED INITIALIZED READY

BOOTING

RUNNING

SHUTTING_DOWN EMPTY DOWN

DYING

DEAD

U:READY

U:RUNNING

U:SHUTTING_DOWN

U:DOWN

Page 28: Solaris (Branded) Zone Internals

状態遷移におけるブランドの扱い

Page 29: Solaris (Branded) Zone Internals

ゾーン毎ブランド情報の初期化

• D:zone_create() 成功時 (= READY 状態への遷移後)に、 必要に応じてゾーン毎ブランド情報を初期化

• ブランド設定は、あくまで「ゾーン属性」の一つ

Page 30: Solaris (Branded) Zone Internals

ゾーン毎ブランド情報の初期化

1. D:zone_setattr() 実行 2. zone(ZONE_SETATTR, ZONE_ATTR_BRAND) システムコールを発行

3. K:zone(ZONE_SETATTR, ZONE_ATTR_BRAND) 実行

4. K:zone_set_brand() 実行 5. 対応する brand_t の引き当て 6. zone_t.zone_brand に設定 7. zone_t.zone_brand->b_ops.b_init_brand_data() を実行

Page 31: Solaris (Branded) Zone Internals

ゾーン毎ブランド情報の破棄

• K:zone_destroy() の一環 (= DEAD状態への遷移後)として、 必要に応じてゾーン毎ブランド情報を破棄

• 初期化と異なり、ブランド情報破棄の要否は、 カーネル側でも判断できる

Page 32: Solaris (Branded) Zone Internals

ゾーン毎ブランド情報の破棄

1. K:zone_destroy() において、 DEAD 状態への遷移を検出

2. ゾーン毎ブランド情報の有無を検査 ※ zone_t.zone_brand の NULL 判定

3. zone_t.zone_brand-> b_ops.b_free_brand_data() を実行

Page 33: Solaris (Branded) Zone Internals

ブランド有効時のプロセス起動

Page 34: Solaris (Branded) Zone Internals

exec() でのブランド処理(1)

1. ブランド設定要否の判定 – 現ゾーンのブランド設定の有無 – 現プロセスのブランド設定の有無 – 明示的なブランド設定指示の有無

2. 必要に応じてプロセス毎ブランド設定を実施 (K:brand_setbrand())

3. ブランド固有の exec 前処理の実施 (BROP(proc)->b_exec()) – Solaris10 ブランドの場合:

• proc_t->p_brand_data->spd_handler のクリア

– 旧 lx ブランドの場合: • コンテキスト関数(後述)の登録 • Linux/Solaris 間での PID 変換テーブルの更新等々

Page 35: Solaris (Branded) Zone Internals

exec() でのブランド処理(2)

1. K:execelf() での実行ファイル読み込みにおいて、ブランド固有処理呼び出し (BROP(proc)->b_execelf())

2. ブランドライブラリの vnode 引き当て (s10_brand.so.1)

3. ブランドライブラリをメモリ空間にロード 4. オリジナル実行ファイルをメモリ空間にロード 5. プロセスエントリポイントをブランドライブラリの U:_start() に変更

6. ユーザ空間での実行を開始 7. ブランドライブラリの U:_start() を実施 8. U:_start() から U:brand_init() の呼び出し

Page 36: Solaris (Branded) Zone Internals

exec() でのブランド処理(3)

1. U:brand_init() 実施 2. U:brand_post_init() 実施 3. brandsys(B_REGISTER) システムコール発行 4. K:brandsys(B_REGISTER) 実施 5. ブランド固有処理として実施

(BROP(proc)->b_brandsys(B_REGISTER))

6. 「ブランドライブラリ中の関数テーブル」のアドレスをカーネル空間に取り込む (proc_t.p_brand_data->spd_handler で参照)

7. オリジナル実行ファイルのELF情報の読み出し (brandsys(B_ELFDATA))

8. 実行ファイルの本来のエントリポイントに遷移

Page 37: Solaris (Branded) Zone Internals

exec() における重要事項

• ブランドライブラリが事前に読み込まれる (e.g. /usr/lib/s10_brand.so.1 相当)

– カーネル内部で実施することで、LD_PRELOAD 書き換え等の影響を受けない

• ブランドライブラリ中の関数テーブルのアドレスが、カーネル内部に記録される

Page 38: Solaris (Branded) Zone Internals

ちなみに……

• 「Solaris 10 オペレーティングシステムでサポートを中止した製品」曰く: – この告知は、32 ビット版の静的システムライブラリおよび静的にリンクしたユーティリティーだけに該当します。64 ビット版の静的システムライブラリやユーティリティーは、これまで提供されたことがありません。

– 32 ビット版の Solaris 静的システムライブラリおよび静的にリンクしたユーティリティーは、Solaris でサポートされなくなりました。特に、静的 C ライブラリ (/usr/lib/libc.a) は、Solaris でサポートされなくなりました。

– http://docs.oracle.com/cd/E19253-01/819-0305/eywvd/index.html

• 静的リンクバイナリへの配慮は無用な模様 • Solaris11 のゾーン運用マニュアルでの「静的にリンクされたバイナリはサポートされません」との明記は何だったのか?(笑)

Page 39: Solaris (Branded) Zone Internals

ブランド有効時の システムコール処理フロー

Page 40: Solaris (Branded) Zone Internals

ブランド固有処理要否の判定

1. システムコールトラップの処理を開始

2. プロセスのブランド設定の有無を確認 (proc_t.p_brand の NULL 判定)

3. 固有システムコール処理の要否を確認 (brand_t.brand_mach_ops 設定の有無)

4. ブランド固有のシステムコール処理に遷移 (brand_t.brand_mach_ops 経由)

Page 41: Solaris (Branded) Zone Internals

Solaris10 ブランド固有処理

1. 関数テーブル登録有無の確認 2. エミュレーション処理(= ユーザ空間)に「復帰」 3. ユーザ空間でシステムコールをエミュレーション

– システムコール呼び出し時引数そのものを、レジスタ/スタック経由で受理可能

– エミュレーションの必要性に応じて、適宜、別のシステムコールの呼び出しも実施

4. エミュレーション処理終了契機で、システムコール発行元アドレスに直接復帰 – カーネルからの復帰時に、戻りアドレスを調整済み

5. システムコール発行元は、そのまま処理を続行

Page 42: Solaris (Branded) Zone Internals

エミュレーション処理の回避

• システムコールのエミュレーション処理を回避したいケースがある – システムコールエミュレーション処理でのシステムコール呼び出し

– ゾーン関連ライブラリ(e.g. s10_npreload.so.1)

• __systemcall() によるシステムコール番号指定でのシステムコール呼び出しを使用 – システムコール番号が 1024 未満なら、エミュレーション処理判定を実施

– それ以外なら、1024 を引いたシステムコール番号で本来の処理を実施(= エミュレーションの回避)

Page 43: Solaris (Branded) Zone Internals

lx ブランド固有処理

• 基本の処理フローは Solaris10 ブランドと同じ

• エミュレーションコード側では、「システムコール番号+1024」無しでのシステムコール呼び出しを実施

• 何故、エミュレーション処理の無限呼び出しが発生しないのか?

Page 44: Solaris (Branded) Zone Internals

ブランドゾーンにおける エミュレーション対象

システムコール 呼び出し形式

Solaris ネイティブ

ブランドゾーン エミュレーション 可能形式

Solaris 系ブランド エミュレーション 対象

(旧) lx ブランド エミュレーション 対象

int 80h × ○ × ○

int 91h ○ ○ ○ ×

sysenter ○ ○ ○ ×

syscall ○ ○ ○ ×

Page 45: Solaris (Branded) Zone Internals

エミュレーション対象形式の隙間

• lx ブランドゾーンのアプリ/ライブラリ – Linux 由来 – システムコールの発行に ”int 80h” を使用 – エミュレーション対象

• lx ブランド固有ライブラリ – Solaris 由来 – システムコールの発行に “int 80h” 以外を使用 – エミュレーション対象外

• lx ブランドゾーンでの実行であっても、Solaris 由来の「lx ブランド固有ライブラリ」が発行するシステムコールは、エミュレーション対象から除外

Page 46: Solaris (Branded) Zone Internals

不遇な sysenter 形式

• Linux における “sysenter” 形式の一般向けサポート開始は 2.6 (released at 2003-12-17)

• OpenSolaris での lx ブランドサポート開始は 2006-09-11 (コミット日時ベース)

• sysenter 形式を lx ブランドでサポートしなかったのは: – lx ブランドのサポート対象は 32bit バイナリのみ

– AMD64 は 32bit モードで sysenter 命令を未サポート

– 「ポータブルな 32bit バイナリ」であれば、sysenter を使わない筈、と判断したのかな?

– 安全策として、ENOTSUP 返却とかの対応ぐらいは入れておいても良かった気が……

Page 47: Solaris (Branded) Zone Internals

エミュレーション可能形式の変遷

システムコール 呼び出し形式

lx ブランド廃止前 エミュレーション 可能形式

lx ブランド廃止後 エミュレーション 可能形式

SmartOS での エミュレーション 可能形式

int 80h ○ × ○

int 91h ○ ○ ○

sysenter ○ ○ ○

syscall ○ ○ ○

Page 48: Solaris (Branded) Zone Internals

SmartOS での “int 80h” 対応の要否

• SmartOS の lx ブランド対応開始に伴い、ブランドゾーンにおける「エミュレーション対象呼び出し形式」として “int 80h” が復活

• Solaris 系ブランド: – OpenSolaris 由来の「エミュレーション要否判定」によるエミュレーションを採用

– “int 80h” 形式はエミュレーションの必要なし

• lx ブランド: – SmartOS 独自の要否判定ルートからエミュレーションルートに分岐 – 全ての呼び出し形式がエミュレーション対応 (32bit/64bit 両対応) – OpenSolaris 由来の要否判定テーブルの “int 80h” 欄は NULL

• エミュレーション対象としての “int 80h” 復活は不要なのでは? – 旧 lx 実装を再利用するため、「lx 実装の破棄」リビジョンを backout – backout の副作用として “int 80h” 対応が復活 – 「動いているコードを変えるな」原則で既存実装を維持、の流れか?

Page 49: Solaris (Branded) Zone Internals

SmartOS でのエミュレーション回避

• SmartOS の lx ブランドは、sysenter や syscall 形式のシステムコールもエミュレーションの対象

• OpenSolaris の lx ブランドのような回避手法が使えない • 以下の手法でエミュレーションを回避

1. ユーザランドのエミュレーション処理への遷移前に、スレッドを LX_STACK_MODE_NATIVE でマーク

2. エミュレーション処理で発行したシステムコールは、スレッドのLX_STACK_MODE_NATIVE 判定により、エミュレーション処理を回避

3. エミュレーション処理終了時は、常にカーネルに戻る (syscall(SYS_brand, B_EMULATION_DONE) を使用)

4. スレッドを LX_STACK_MODE_BRAND でマーク 5. システムコール呼び出し元に復帰 6. LX_STACK_MODE_BRAND スレッドなので、以後のシステムコールは

エミュレーション処理の対象に

Page 50: Solaris (Branded) Zone Internals

ブランドエミュレーション要否判定による 性能劣化の回避

Page 51: Solaris (Branded) Zone Internals

SPARC (v9) の場合

• 以下の契機で、システムコールテーブルを、 システムワイドに書き換える

– 最初のブランドモジュールの読み込み

–最後のブランドモジュールの破棄

Page 52: Solaris (Branded) Zone Internals

ia32 の場合

• コンテキストスイッチ時に実行される関数群を lwp 毎に追加できる機能を利用

– コンテキストスイッチの都度、システムコールテーブル周りの設定を書き換える関数を登録

– Ia32 ディレクトリ配下のファイルの割には、 AMD64 対応コードも含まれている模様

Page 53: Solaris (Branded) Zone Internals

ユーザ空間での ブランドエミュレーション実施の是非

Page 54: Solaris (Branded) Zone Internals

ユーザ空間エミュレーションは遅い!

• そもそも Solaris はシステムコールのオーバヘッドが大きい – 「OS毎のシステムコール実行性能 」 by 藤原

http://d.hatena.ne.jp/flying-foozy/20140514/1400090130

• 技術的な理由も色々推測はできる – エミュレーション処理にバグがあっても、被害の影響範囲が、ユーザ空間で閉じる

– なんなら、デバッガでアレコレできる

• しかし、どう考えても、ユーザ空間エミュレーションは性能的に不利

Page 55: Solaris (Branded) Zone Internals

むしろライセンス的な理由?

• lx ブランド有効時に Linux 準拠のロジックがカーネル空間に置かれるのがまずいのかも?

• Ubuntu がカーネルモジュールとして ZFS を同梱する際にも、反対意見が諸々あった模様 – “Interpreting, enforcing and changing the GNU GPL, as

applied to combining Linux and ZFS” by Richard Stallman https://www.fsf.org/licensing/zfs-and-linux

– “GPL Violations Related to Combining ZFS and Linux” by Bradley M. Kuhn and Karen M. Sandler http://sfconservancy.org/blog/2016/feb/25/zfs-and-linux/

Page 56: Solaris (Branded) Zone Internals

SmartOS における lx ブランド

• 冒頭で述べたように、Joyent の SmartOS では lx ブランドのサポートが再開された

• しかも、一部のシステムコールはカーネル内でエミュレーションを実施!

• 「ユーザランドエミュレーションはライセンス問題回避のため」との考察は間違っていた?

• カーネル内エミュレーションは以下の様なもの限定っぽいので、もしかして「“ロジック”は入ってないでしょ?」と強弁するつもりか?(笑) – フラグの値読み替え: e.g. open(2) – 構造体メモリレイアウトの辻褄あわせ: e.g. stat(2)

Page 57: Solaris (Branded) Zone Internals

ネイティブブランド実行の強制

Page 58: Solaris (Branded) Zone Internals

Solaris10 ブランドでの実現方式

• 起動用ラッパースクリプト

– s10_isaexec_wrapper スクリプト

– s10_python_wrapper スクリプト

• 「ネイティブブランド実行」表明用スクリプト

– s10_native スクリプト

• コマンド列補正処理ライブラリ

– s10_npreload.so.1

Page 59: Solaris (Branded) Zone Internals

プロセス起動

1. 起動用ラッパースクリプト経由でコマンド実行 2. 以下の引数で exec() システムコールを実施

– s10_native – ld.so.1 – ld.so.1 向け -e オプション指定 (s10_npreload.so.1 含む) – 本来のコマンド+引数

3. ラッパースクリプトはブランド付きプロセスなので s10_brand.so.1 が事前ロード済み

4. s10_brand.so.1 の exec() のエミュレーション処理に遷移 5. 実行コマンドが s10_native の場合:

– argv[0] を破棄 ⇒ ld.so.1 が実行コマンドに – brand(B_EXEC_NATIVE) システムコール経由で ld.so.1 をネイ

ティブブランドで実行

Page 60: Solaris (Branded) Zone Internals

起動後処理

1. ld.so.1 による s10_npreload.so.1 読み込み

2. s10_npreload.so.1 の init() 実行

3. brand(B_S10_NATIVE) システムコール実行

4. proc_t のコマンド行/引数情報から、ld.so.1 周りの情報を破棄

5. 以後、以下のコマンドでも、本来のコマンド行情報を取得可能 – ps

– pgrep

Page 61: Solaris (Branded) Zone Internals

何故こんなに複雑なのか?

• ネイティブ実行の強制にはワンクッション必要 – ブランドの設定は exec(2) 契機でのみ実施

– LD_PRELOAD 機能での init() 処理は、実行ファイルの exec(2) 後の実施

• ワンクッション置く=ラッパースクリプト経由だと、ps/pgrep 等が上手く機能しない – 辻褄あわせが必要 ⇒ brand(B_S10_NATIVE)

• 通常の exec(2) ルートだと、必要のない、エミュレーション用のブランドライブラリのロードが発生 – 回避ルートが必要 ⇒ brand(B_EXEC_NATIVE)

Page 62: Solaris (Branded) Zone Internals

ゾーンの外から中へ

Page 63: Solaris (Branded) Zone Internals

実現方式

• zone_enter() システムコールを利用 –現行プロセスの所属ゾーンを変更

–グローバルゾーンからの変更のみ許可

–ブランド設定は無し

• zone_enter() 利用の実例 – zlogin コマンド (-C 無し時)

– wall コマンド

–ゾーンへのパッケージインストール時の排他 (“pkgadm lock -a” をゾーン内で実行)

Page 64: Solaris (Branded) Zone Internals

zlogin の処理フロー

1. 擬似 tty の確保 2. fork() システムコール実施(以下、子プロセス側処理) 3. 擬似 tty のセットアップ 4. zone_enter() システムコールの実施

– 所属ゾーンの変更 ⇒ ファイル参照はゾーンルート相対 – ブランド設定は維持 ⇒ システムコールは Solaris ネイティブ処理

5. 「ログイン」コマンドを exec() システムコールで実行 (コマンド列は当該ゾーンの config.xml から入手) – lx ゾーンなら login –h zone: ZONENAME USERNAME – solaris 系ゾーンなら login –z ZONENAME USERNAME

6. 「ログイン」由来プロセスはゾーン側に所属(ブランド付けアリ) 7. 親プロセス側は、グローバル側入出力と、擬似 tty の間で I/O 仲

介をループ

Page 65: Solaris (Branded) Zone Internals

zone_enter() プロセスの PID

• zone_enter() 時に proc_t.p_flag |= SZONETOP

• “process has no valid PPID in its zone”

• proc_t 構造上の「親プロセス」参照は維持

• ユーザ空間向けに PPID を返す際には zsched の PID で差し替える – /proc/<PID>/psinfo 実装

– getpid(2) 実装

• zone_enter() 以外の SZONETOP 設定契機 – exit(2) 実装: 子プロセスの親プロセス切り替え

Page 66: Solaris (Branded) Zone Internals

ゾーン向け「コンソール」の実現

Page 67: Solaris (Branded) Zone Internals

zlogin –C の処理フロー

1. zoneadmd 起動の保証(※ 理由は後述)

2. UNIX ドメインソケットを以下に conect() /var/run/zones/ZONENAME.console_sock

3. コマンドラインの入出力と connect() 済みソケットとの間での I/O 仲介をループ

4. ※ 基本的にゾーン側処理には介入しない!

Page 68: Solaris (Branded) Zone Internals

zoneadmd による擬似コンソール

1. ゾーン向け仮想コンソールデバイスの作成

– /dev/zcons/ZONENAME/masterconsole (master)

– /dev/zcons/ZONENAME/zoneconsole (slave)

2. UNIX ドメインソケットを以下に bind() /var/run/zones/ZONENAME.console_sock

3. 仮想コンソールデバイスと bind() 済みソケットとの間での I/O 仲介をループ

4. ※ 基本的にゾーン側処理には介入しない!

Page 69: Solaris (Branded) Zone Internals

コンソール連携のシステム構成

• zlogin プロセス ⇔ zoneadmd – UNIX ドメインソケットによる擬似コンソール

– /var/run/zones/ZONENAME.console_sock

• zoneadmd ⇔ ゾーン内コンソール処理 – zcons デバイス

– /dev/zcons/ZONENAME/masterconsole (master)

– /dev/zcons/ZONENAME/zoneconsole (slave)

• zoneadmd を介した間接アクセスなのは、zlogin プロセス終了によるコンソール切断等の影響を排除するためか?

Page 70: Solaris (Branded) Zone Internals

ブランド毎コンソール構成の抽象化

• グローバル側仮想コンソールデバイス – /dev/zcons/ZONENAME/masterconsole (master)

– /dev/zcons/ZONENAME/zoneconsole (slave)

• platform.xml による構成設定 – 改名設定:

• グローバル側: /dev/zcons/ZONENMAE/zoneconsole

• ゾーン側: /dev/zconsole (Solaris系)又は /dev/console (旧 lx)

– シンボリックリンク設定 (e.g. Solaris 系ブランド): • リンク先: zoneconsole

• リンク元: console, syscon, sysmsg, systty

Page 71: Solaris (Branded) Zone Internals

zoneadmd によるコンソールへの通知

1. zoneadmd 経由で状態を変更

2. イベント通知用内部 pipe にメッセージ書き込み

3. コンソール I/O 仲介ループで pipe への書き込みを検出

4. “zlogin –C” 連携用 UNIX ドメインソケットへのメッセージ書き出し

5. “zlogin –C” 側でメッセージの書き込みを検出

6. 読み込んだメッセージのユーザ側 tty への書き出し

Page 72: Solaris (Branded) Zone Internals

WINDOWS ブランドゾーンの可能性

Page 73: Solaris (Branded) Zone Internals

懸念事項

• 実行可能ファイルの読み込み – Windows の実行可能ファイル(.EXE)の形式は Solaris 標準の ELF 形式ではない

• システムコールのエミュレーション – 呼び出し ABI の互換性

– パス区切りや、ドライブ文字、予約ファイル名等の、ファイルアクセス周りは、システムコールエミュレーションで吸収可能な筈

• ウィンドウ/コンソールの取り扱い

• その他

Page 74: Solaris (Branded) Zone Internals

Solaris における「実行可能ファイル」

• Solaris は「実行可能ファイル」種別毎にカーネルモジュールを提供 • Solaris では以下の種別が実装&予約済み

– a.out 形式ファイル (SPARC 後方互換向け) – ELF 形式ファイル – JAR 形式ファイル – shebang (“#!”) 付きスクリプトファイル – シェルバイナリ形式ファイル (“ksh¥0” で始まるファイル)

• ファイル冒頭のマジックナンバー一致で種別判定 • カーネルモジュール追加により、新規種別を追加可能 • 新規に追加可能な種別は、カーネルビルド時固定

– OpenSolaris ソースベースでは4つ – マジックコード一致の線形探索だから、数を増やしたくないのかな?

Page 75: Solaris (Branded) Zone Internals

EXE ファイル対応する場合

• EXE ファイル向けの実行処理を、カーネルモジュールとして実装

• Windows ブランド設定時のみ実行を許可 • ブランド固有処理の一環で以下を実施

– ブランド固有ライブラリの事前読み込み – Windows *.DLL と UNIX *.so 周りの差異の辻褄併せ

• EXE 形式でも、ブランド固有処理で呼び出す関数エントリが b_elfexec() なのはご愛嬌(笑)

• EXE 形式解釈ロジックがカーネルモジュールに含まれる点は、ライセンス的にちょっと不安

Page 76: Solaris (Branded) Zone Internals

システムコールABIの互換性

• Win XP 以降のシステムコール呼び出し

– sysenter(@32bit)

– syscall(@64bit)

• x86/AMD64 Solaris も同じ方式

–常にブランド固有処理にルーティングされる

–仮に引数/戻り値周りの扱いが違っても、ブランド固有処理で吸収可能な筈

Page 77: Solaris (Branded) Zone Internals

ウィンドウ/コンソールの扱い

• zlogin –C や対話的 zlogin は駄目だろうなぁ…

• 「非対話的 zlogin 実行での CUI コマンド実行」程度なら何とかなるか?

• GUI を使うなら、Windows ブランドゾーン内で以下のようなプロセスの稼動が必要な筈

–ウィンドウマネージャ

– リモートデスクトップサーバ

Page 78: Solaris (Branded) Zone Internals

その他

• bare metal Windows と同等に使うためには、他にも多数の構成要素が必要 –認証処理 (ログイン系)

–承認処理 (権限管理)

– ファイルブラウザ for GUI

–ユーザランド DLL (user32.dll, kernel32.dll etc...)

• Free Software で構成可能な lx コンテナと違い、必要なバイナリ一式を持ち込むのは、かなりグレーゾーンぎりぎりな気が……

Page 79: Solaris (Branded) Zone Internals

結論

• ネタとして考える分には「Windows ブランドゾーン」は興味深い

• CUI コマンドを zlogin 経由で非対話的に実行するあたりまでは、比較的実現性がある

• それ以上の領域は、技術的にも面倒だが、ライセンス的にヤバそう

• 単にアプリを動かすだけなら、Wine を使う方が妥当な感じが…… (https://wiki.winehq.org/Wine_Developer%27s_Guide) – chroot 併用で、コンテナっぽい雰囲気も出せる筈

Page 80: Solaris (Branded) Zone Internals

ご清聴ありがとうございました