ルートセッションとログインセッション


初期の頃から、Mac OS Xは安全なオペレーティングシステムとして設計されてきました。この安全性の方針の一つが、プロセス間通信に及ぼすコントロールです。

プロセス群はグループへと緩やかにまとめられ、ルートセッションかログインセッションのいずれかに関連付けられます。

ブートストラップサーバ【bootstrap server】としても知られている、mach_initプログラムは、誰がいつプロセスを作成したのか、といった事柄を元にして、各新規プロセスを適切なセッションへと割り当てます。

ルートレベルプロセスのほとんどはルートセッション【root session】へ配置されます。ルートセッションは最初に作成され、最後に破壊されるセッションです。システム上に存在できるルートセッションは常に一つだけであり、そこにはほとんどの起動時プロセスとデーモンが住んでいます。

ルートセッションにいるプロセスは、システムのすべてのユーザにサービスを提供することが許可されています。たとえば、lookupdおよびmDNSResponderプロセスは、どちらもルートセッションで動作します。これらのプロセスはユーザから独立しており【それとも「ユーザ独自」?user-independent】、システム上の誰にでも基本的なサービスを提供します。

ユーザによって起動される、もしくはユーザの利益のために起動されるプロセスは、ログインセッション【login session】の中に住みます。各ログインセッションは認証されたユーザと関連付けられます。システムは、時間が与えられるといつでもアクティブになる、複数のログインセッションを持つことができますが、その一つ一つは関連付けられたユーザごとに孤立した何かです。【この辺訳が怪しい】

コンソールログインセッションには、FinderやDockといったプロセスが含まれます。対して、リモートログイン接続はシェルレベルプロセスだけを含みます。異なるログインセッション間の通信のほとんどは、ブートストラップサーバによって制限されています。通信は今でも可能ではありますが、一般的には明示的な、信用された【認証された?trusted】接続を作成することが要求されます。

重要:この記事の内容は、デーモンやその他の低レベルプロセスを書いている開発者向けを主に意図しています。他の開発者はこうした情報に興味を持つかもしれませんが、一般的な開発の工程においては必要とすべきではありません。また、これらのセクションはDarwin Documentationの、Kernel Programmingで説明されている、Machカーネル環境の知識があることを前提としています。

ユーザ空間の安全を確保する

プロセス群をルートセッションとログインセッションへとまとめることで、mach_initプロセスはユーザのためにより信頼できる環境を作り出すことができます。Machポートへの要求は、mach_initプロセスを通って、適切な目標へ送り届けられます。ログインセッションはユーザのプロセスを取り囲む壁の集まりのような役目を果たし、現在のログインセッションやルートセッション内のこれら【プロセス?】へのポート要求を制限します。そうすることで、異なるセッション内のプロセスによる、現在のログインセッション内のポートへのアクセスを取得しようとする、不慮もしくは故意の試みを防ぎます。ある意味、ログインセッションは、これらが表すプロセスを取り囲んでいる、軽量なファイアウォールといえます。

従って、ログインセッションは他のユーザによって起動されたプロセスが、ユーザ自身のプロセスを邪魔しない、ということの保障をユーザに与えます。たとえば、悪意のあるユーザが、既知のユーザサービスであると偽装するプログラムを書き、他のユーザから情報を集めるためにそのプログラムを使用することがあるかもしれません。けれども、その悪意あるユーザがそのマシンの管理者アクセスを持っている場合を除いて、そのプログラムは悪意あるユーザのログインセッションで動作します。他のユーザはそのプログラムが見えないので、彼らはその影響から守られます。

ログインセッション間の通信

起動時に、カーネルはMachポートへのルックアップ要求の処理が仕事である、mach_initプロセスを起動します。各要求の部分に応じて、mach_initはプロセス群がログインセッション境界を不正に越えようとしないことを保障もします。これは、セッション境界を越えることが完全に禁じられていると言っているわけではありません。それが適切で、なおかつ必要であるという状況も存在します。たとえば、アプリケーションは他のセッションのプロセスと通信するために、BSDソケット、共有ファイル、共有メモリ、もしくは配布される通知【distributed notifications】を使用することができます。けれども、そうすることは両方のセッションの間の協調を必要とし、ある程度の水準の信用を伴います。

概念上はルートセッションを、それに続くすべてのログインセッションの親セッションとして考えることができます。けれども、親子の例えはそこで終わりです。ルートセッション内のプロセスは、いかなるアクティブなログインセッション内のプロセスについても生来の知識を持ちません。逆に、ログインセッション内のプロセスはルートセッション内のプロセスの知識を持っており、適切なサービスの必要に応じてこれらにアクセスすることができます。ですが、一つのログインセッション内のユーザプロセスは、他のログインセッション内のプロセスへの生来のアクセスを全く持ちません。

図 1 ルートセッションとログインセッションの関係

ルートセッション内のプロセスは、完全にログインセッション内のプロセスの裏にあります。ユーザプロセスがルートセッション内で実行しているデーモンからサービスを要求したとき、プロセスは一般的に結果を返すだけでなく、ポートアドレスを提供します。対話するための明確なMachポートを持つため、デーモンは要求の結果をユーザプロセスへ直接送り返すことができます。図 2はこの振る舞いを図示しています。

セッションの生存期間

あなたが予想できる通り、ルートセッションはシステム上で最初に作成されます。またシステムがシャットダウンされるときに最後に破壊されます。ルートセッションはすべてのユーザに適用されるデーモンとシステムサービスを実行するための論理的な場所です。けれども、ルートセッションで動作するデーモンを書くことは、ウインドウサーバの存在を必要とする、多くの高レベルシステムフレームワークの使用を不可能にします。(あなたが使用できるフレームワークの一覧については、「ルートセッションにおいて利用できるフレームワーク」(12ページ))を参照してください。

ユーザがログインしたとき、ログインウインドウかsshのいずれかを通じて、システムは新しいログインセッションを作成します。ログインセッションは、そこに属するすべてのプロセスが終了されるまで存在し続けます。ユーザがログアウトするとき、システムはユーザのログインセッション内のプロセスを終了しようと試みます。ログインセッション内の最後のプロセスが死んだとき、システムはログインセッションを閉じて、そのメモリを回収します。

注:もし、ユーザプロセスがログアウトする前に自分自身をデーモン化すると、ユーザのログアウトが終了した後も生き続け、ログインセッションの存在を延長します。Apacheウェブサーバといったシステムサービスのいくつかは、ログアウトの間にシャットダウンされることを避けるためにこれを行います。ユーザのログアウトを生き延びたプロセスは、ログインセッション内で実行を継続します。けれども、これらはユーザがログアウトしたときに終了される、ウインドウサーバの存在がなくても実行できなければいけません。

アプリケーションは、System Startup Programming Topicsで説明されている、さまざまな理由のためにユーザのログアウトを延期することができます。ユーザ単位のサービスは自動的にシャットダウンされ、ログアウトの手続きを中断する機会は与えられません。

ログインセッションを見分ける

ユーザがシステムで認証されるたびに、システムのSecurityレイヤはユーザのログインセッションを識別するためにユニークなIDを作成します。このIDがセキュリティセッションIDであり、しばしば単純にセッションIDとして参照されます。アプリケーションは、異なるログインセッションに割り当てられたリソース同士を区別するためにこのセッションIDを使用できます。

セッションIDはユーザのログインとログインの間で持続性がありません。各セッションIDは、現在のログインセッションの存続期間の間だけ、そしてそのログインセッション内のすべてのプロセスが有効である間だけ有効です。もしユーザがログアウトしてからログインしなおすと、新しいログインセッションには別のセッションIDが割り振られます。

注:セッションIDは、setsid関数を呼ぶことで作られる、setsidセッションと混同されてはいけません。この二つの値は異なり、異なる目的のために用いられます。セキュリティセッションIDは、与えられたセキュリティセッションの内部で起動されたすべてのプロセスを包含しており、複数のsetsidセッションを含んでいることがあります。

あなたは他のログインセッション内のオブジェクトとの名前空間の衝突を防ぐためにセッションIDを使用すべきです。もし、あるアプリケーションがセッション固有の共有メモリ領域のために共通の名前を使用していたら、別のセッションにおける同アプリケーションは、同じ名前を用いてメモリ領域を作成しようとしたときに、エラーに出くわすことでしょう。メモリ領域の名前にセッションIDを含めることで、こうした種類のエラーを防ぐことができます。

セッションIDをどうやって取得するかの詳細は、「ログインセッション情報を取得する」(14ページ)を参照してください。

ログインセッションは開発者にどのように影響するか

もし、あなたがエンドユーザ向けのアプリケーションを書いているのであれば、ログインセッションの存在はあなたの設計により熟考を必要とするでしょう。あなたは、あなたのアプリケーションの複数インスタンスが同時に実行されているかもしれないということを覚えておく必要があり、あなたのコードをリソース競合の可能性に対処できるように書かなければいけません。もし、他のログインセッション内のプロセスと通信する必要があれば、あなたはBSDソケット、配布される通知、その他の形式の信頼された接続が必要となります。

もし、あなたがカーネルコードだけを書いているのであれば、ルートセッションとログインセッションは大部分は無関係です。けれども、ドライバ開発者はカーネルもしくはルートセッション内のどちらかで実行する、もしくは、一つかそれ以上のログインセッション内のプログラムと通信するプログラムを書く必要が頻繁にあります。たとえば、ドライバ開発者はアクティブなユーザの環境設定を元にサウンドドライバを設定したいという場合があることでしょう。この場合には、各ログインセッション内で実行する仲介プログラムを書き、ユーザレベルのドライバへの変更を通信する必要があるでしょう。

ルートセッションにおいて利用できるフレームワーク

システムフレームワークの多くは、その実装の一部でウインドウサーバに依存しています。これらは画像の描画、通知の追跡、その他の方法でシステムと調和するためにウインドウサーバを使用します。ほとんどのアプリケーションについては、これは全く問題ではありませんし、いくつかの振る舞いを実装するために実際に必要としています。けれども、もしあなたがデーモンやその他の種類のルートセッション内で動作するプログラムを書いているのであれば、これらと通信するウインドウサーバプロセスはありません。結果として、高レベルフレームワークの多くは全く使われません。