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

Solaris (Branded) Zone Internals

Apr 14, 2017

Download

Software

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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

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