第3章 Sambaアーキテクチャ

Dan Shearer

November 1997

目次

概要
マルチスレッドと Samba
smbd のスレッド化
nmbd のスレッド化
nbmd のデザイン

概要

この文書は、内部的に Samba がどのように動くかについての一般的な概要を説明している。 Samba Team は、非常に汚い SMB と CIFS プロトコルによって押しつけられる、優雅さ、セキュリティと 制約の間で、最も良い妥協点であるモデルを見いだそうとした。

また、以下のようなよく聞かれる質問のいくつかに答えようともしている:

  1. UNIX 上で動かすときに Samba は安全か? xyz プラットフォームでは? root 特権についての問題は?

  2. Sambaのいくつかの部分中でのマルチスレッドの賛否

  3. 名前解決を行う WINS やブラウズ機能は、なぜ別プロセスとしないのか?

マルチスレッドと Samba

人々は時々、一様にスレッドを良いものとして推奨する。それらは推奨する人にとっては とても良いものであるが、smbd に取ってはまったく不適当である。nmbd は別の問題で、 マルチスレッドそれ自身はとても良いことである。

手短に言うと、smbd はマルチスレッド化されておらず、UNIX 配下での別のサーバー(例えば、 書いている時点においては Syntax) はこの方法を取っていて、とても大きな性能上の問題を 抱えていて、頑丈ではない。nmbd もスレッド化されていないが、これは、35以上もの プラットフォームにまたがって、コードの整合性を取り、ポータブルにするということが、 不可能だからという理由である(この欠点は同じく smbd をスレッド化することにも当てはまる)。

より長い期間において、smbd をマルチスレッドにしない、とても良い理由が存在している。 マルチスレッドは Samba をより遅くし、スケーラビリティを減少させ、移植性をなくし、 とても不安定にさせる。実際、各接続に対して独立したプロセスを使用していて、それは Samba の最も大きな利点の1つである。

smbd のスレッド化

スレッド化した smbd から発生するいくつかの問題は以下の通り:

  1. プロセスの代わりにスレッドを作成するだけでなく、スレッド固有で存在しなければならない 場合、すべての変数について管理を行わなければならない(現在それらはグローバルである)。

  2. もしもある1つのスレッドが異常終了すると(例えば、セグメンテーションフォルト)、 すべてのスレッドが異常終了する。信頼性を損ねることになる。

  3. 使用している多くのシステムコールがブロックしている。多くのシステムコールで ブロックしないか同等のことをするものは、用意されていないか、うまく使えない (そして遅い)。そのため、ある1つのスレッドでブロックしている間は、すべての クライアントは待たされることになる。ある共有が遅い NFS ファイルシステムで、 その他が早いものだと仮定してみた場合、すべてのクライアントが、NFS のスピードに 抑制されてしまう。

  4. 異なったスレッド中で異なった uid として動作できない。これは、すべての SMB パケット上で uid/gid をスイッチしなければならないと言うことを意味する。それはとてつもなく遅い。

  5. プロセスあたりのファイルディスクリプタ制限は、制限されたクライアントの数のみを サポートできることを意味する。

  6. プロセスに対して fcntl() のコンテキストでロッキングを行うようにスレッドに対して システムロッキングを使うことが出来ない。

nmbd のスレッド化

これは理想ではあるが、ポータビリティの要求により実現するのは難しい。

Andrew は ANSI-C の範囲のみを使って(setjmp と longjmpを使って)nmbd 用のテスト用 スレッドライブラリを加工とした。残念なことに、いくつかの OS では、スタック上の 現在のアドレスよりも浅い位置のアドレスを呼び出すために longjmp を制限することに よって、うまくいかなかった(見たところでは、AIX がそうである)。これは、真に ポータブルなスレッドライブラリを不可能にしている。そのため、現在対応している すべてのプラットフォームに対して、nmbdのコードをスレッドあり、なしの両方用意 しなければならず、スレッドの本来の目的はコードをきれいにすることであるが、 それを得られない(スレッドが物事を早くするというのは神話である。スレッドは 再帰のようなものであり、物事をきれいにするが、他の何らかの方法によって、いつでも それはもっと早くすることもできる)。

Chris は、スレッド対独立したプロセス(対他の方法?)を要約する汎用的なデザインを設計 しようとし、いくつかの汎用 APC を通して、それらをアクセス可能にした。これは、 プロトコルによるデータ共有要求(現在のパケットに将来のパケットが依存するなど)という 理由でうまく動かなかった。少なくとも、コードは動いたが、非常にぎこちなく、 その上、fork() タイプモデルは、UNIX 上では決して動かなかった(nmbd に対して、 それが動く OS はあるのだろうか?)。

fork() は安っぽいが、受信したすべての UDP パケット上で処理を行うのに、十分 安っぽいわけではない。プロセスのプールを持つことは可能であるが、プロセス間で、 (複雑な構造体中で)共有された巨大な量のデータのために、プログラムをきれいにする ことは、ひどく難しい。各プラットフォームが共有メモリシステムを持つ事を あてにすることは出来ない。

nbmd のデザイン

もともと、Andrew は、途方もなくスタックを使い、全くもってデバッグ作業を混乱させる、 マルチスレッド環境をシミュレートするための再帰を使った。Luke Leightonは 各パケット上で ステート情報を保持する問い合わせシステムを使うように書き直した。最初のバージョンは、 すべての待機状態ステートによって使われる単一の構造を使っていた。この構造の初期化は、 引数を追加することで行われ、機能が開発するに従い、だんだん複雑化していった。 そのため、より高位の関数と、ユーザーが定義したメモリブロックを指定するポインターによって 置き換えられた。これは突然物事をより簡単にした:非常に多数の関数は静的に作成され、 モジュール化された。これは NTカーネルで使われているものと同じ原理であり、単一の プロセス中ではあるが、スレッドと同じような効果が成し遂げられた。

次に、Jeremy は nmbd を書き直した。nmbd 中のパケットデータはネットワーク上のものではない。 この形式は、処理に対してとても従順なものであるが、まだ他のパケットの内容を保持していた。 nameserv.h中の"struct packet_struct"を参照のこと。そこにはすべての詳細があるが、 ネットワーク上のメッセージについては記述がない。これは、ブラウジングと WINS サポートの ための、ディスクかメモリベースのデータベース中で理想的に使えるようにする。