SambaでWindows NT Server と同じ転送速度を確保する方法

奥山 健一
$Date: 2011-05-25 01:25:09 +0900 (Wed, 25 May 2011) $
$Revision: 1.6 $

1. Background.

Linux や FreeBSD 上に Samba をインストールし、 Windows client のファイルサーバーや printer server として使おうとする場合、 ときどき、信じられないほど低速な転送速度でしか データを転送できないことがあります。

もし、この転送速度が Windows NT Server などとの通信においても 同じ転送速度しかでない場合はある意味諦めがつくこともありますが、 Windows NT Server との通信は速いのに、 Samba との通信だと遅くなる…という場合は、心理的に我慢がなりません。

このドキュメントでは、 smb.conf を適切に書き換えることでこのような状態においても、 Windows NT との通信とほぼ同じ速度の通信速度を確保する方法を述べます。

2. Why do we loose speed against NT?

Samba は Unix マシン上にインストールされます。 そして、Samba はその Unix マシンの持つ TCP/IP 層を使って、 client と通信します。 一方 Windows の client は、 Winsock2 の TCP/IP 層を使って Samba server と通信します。

一般に Unix マシンは TCP 層を RFC に厳密に従って実装します。 このため指定された TCP Window の許す限り、IP packet を送信します。 Ack は「次の受信準備ができた」ことを意味し、 Ack が到達したら転送領域 Window をずらして 送信可能な領域を転送しようとします。 この 転送領域 Window のバイト数を Send Buffer Size と言います。 smb.conf の socket options 中に指定する、SO_SNDBUF 引数は、 この 転送領域 Window のサイズを指定します。

Unix の場合、このサイズは極めて大きく、 例えば FreeBSD は 16384 Byteがデフォルトの Send Buffer Size です。 Windows マシンの多くはこのような大きな Send Buffer Size に耐えられません。 このため、smb.conf の socket options の引数を使って、 Send Buffer Size を小さくします。 従来は 8192byte などがよい、とされてきました。 この大きさは多少であれば、 TCP が通信するに従って最適な値に修正していきます。

しかし、一部の Network Interface Card(NIC) のせいなのか、 NIC Driver の問題なのか、 あるいは Winsock2 の問題なのか、 Windows の問題なのかは判りませんが、 一部の NIC カードを使った Windows では、 なぜか data packet を時々取りこぼす という状態が発生します。 Recieve Buffer Size としては十分余裕があるはずなのに、 取りこぼすことがあるのです。

data packet を取りこぼしてしまうと、再送を要求することになります。 この 再送要求 何バイト目まで受信できたのか を表す Ack Packetが複数回転送されてくることで行われます。 Samba 側 (正確には Samba が動いている Unix マシンの TCP ドライバー) は、 これを受信するととりあえずpacket を再送します。

Windows が取りこぼした Packet は1つだけですので、 Samba から dataが送られてくると、 Ack は一気に数 Packet 分先に進みます。 そして、しばらくすると(と言っても、ほんの10数ミリ秒後には)、 また Windows は Packet を取りこぼします。

これが数回繰り返されると、 Samba 側の TCP 層は次のように想像します。

「もしかしたら、ときどき Packet の到着が遅れることがあるのかも知れない。 再送要求が出ているが、少し待ってみよう。」

そして、1秒程何も送らない、ということをやってみます。 しかし結局状況は改善せず、 Samba 側の TCP 層は結局 data packet を再送します。

Windows が問題を起す場合、 結果として 0.1 - 0.3 sec 程データを転送しては 1秒待つ、 ということが繰り返されます。 しかもこの 1秒 は実際には待つ度に少しづつ時間を伸ばしていくのです。 結果として本来出せるはずの転送速度の 1/20 程度しか実質転送速度が出ない という状況が発生します。


どんな OS をサーバーに使っても必ずこれが起こる、 というのであれば特筆すべき問題ではないのですが、 実はこの問題は Windows NT をサーバーとする場合に発生しない場合があります。 この場合、Samba Server はまるで Windows NT よりも パフォーマンスが悪いように見えます。

残念ながら Winsock2 のソースコードは公開されていません。 Windowsのソースコードも公開されていません。 そのため、正確にはどうしてこのような現象が起こるのか、は判りません。 しかし、tcpdump や WinDump などのツールによって、 ある程度 Winsock2 同士の通信がどのような性質があるのか、が判ります。 少くとも、私は次のような性質がある、と理解しました。

  1. 通信している一方だけが送信したいデータを持っている場合、送信側は:
    1. Send data packet
    2. Send data packet
    3. Wait for Ack arraival (この Ack は上の 2 まで受信したことを表す )
  2. 通信している双方が送信したいデータを持っている場合、
    1. Send data packet
    2. Recv data packet (この packet には 1 の packet に対する Ack が含まれているはず)
    3. Send data packet
    4. Recv data packet (この packet には 3 の packet に対する Ack が含まれているはず)
  3. 上記の I と II の通信パターンを切り替えるときは、 0.1sec 程の「待ち」が必ず入る。

この Winsock2 の性質をクリアしさえすればよい、 という発想で NIC Driver が書かれていると、 NT をサーバーとすると速いのに、Samba だと転送速度が遅い という現象が起こるのだろう、と予測されます。

3. How to set smb.conf

NT だと速いのに、Samba Server だと遅い…という場合に、 せめて NTと同じパフォーマンスを出すように smb.conf を設定するには、 いくつかの情報が必要になります。

まず最初に、Samba Server マシンでの root 権限が必要になります。 smb.conf を編集するわけですし、大抵の場合これは問題ないと思います。

次に Samba Server マシン上で tcpdump を使えなくてはいけません。 root であれば実行権限はありますが、 tcpdump (あるいは、Windows マシン上での観察を行う場合は WinDump ) の使い方を知らなくてはいけません。1つしか NIC が刺さっていなければ

# tcpdump > logfile

で十分ですが、複数の NIC が刺さっている場合、 どの NIC のデータをとるのか? などを指定できる必要があります。 tcpdump のマニュアルを良く読んで、使い方を理解してください。 あと出力結果も読めた方が良いでしょう (ただし、これはある程度経験を積むと、 欲しい情報は把握できるようになります)。

さらに、 問題の Windows マシン の OS, Internet machine 名、そのマシンを操作する権限が必要になります (操作できないとパフォーマンステストができません)。

あと、何か適当なファイル、 5Mbyte から10Mbyte ぐらいのファイルを用意してください。 これは転送速度を計測する際に用います。


さて、以下の例では Samba Server のマシン名を 濃紫(Nohshi.my.domain) 、 Windows client (Windows98) マシン名を 菖蒲(Ayame.my.domain) としましょう。


まずは、Nohshi 上で tcpdump を起動してください。 記録を後で調べる必要があるので、root 権限で

# tcpdump > logfile

を実行しておいてください。

次いで、Ayame 上から Nohshi を開いてください。

Nohshi から Ayame へのファイル転送を行ってください。 上記の 5Mbyteほどのファイルを転送するのであれば、 ウィンドウを Drug&Drop でコピー してもいいですし、 ftp しても構いません。 これは Winsock2 の問題であって SMB protocol 等の問題ではないので、 TCP 通信でありさえすれば何を使っても構いません。

ファイル転送が終了したら、tcpdump を Ctrl-C などで止めてください。 kill -9 では止めない方がいいです。 最後の方の記録が出力されない可能性がありますので。

logfile 中には tcp の通信記録が保存されているはずです。 この中から、

  1. Ayame 、 Nohshi 間の通信記録で、
  2. <mss xxxx> (xxxx の部分は数字)という文字を含んでいる行を 探しだしてください。
Ayame.my.domain > Nohshi.my.domain
という文字列を含んでいる行と、
Nohshi.my.domain > Ayame.my.domain

という文字列を含んでいる行の両方があると思いますが、 その両方を調べて、xxxx という数字の 小さい方 を選んでください。 この数字のことを Maximum Segment Size mss と呼びます。 普通、ethernet を使っている場合は、 1460 か 536 かのどちらかでしょう。 これは、一つの IP packet に入れることのできる、 最大の data 量を表します。

smb.conf の [global] 部の最後に

include = <smb.conf のあるディレクトリ>/smb.conf.global.%a
include = <smb.conf のあるディレクトリ>/smb.conf.global.%M
include = <smb.conf のあるディレクトリ>/smb.conf.global.%a.%M

という行を追加してください。

<smb.conf のあるディレクトリ> は FreeBSD ports の場合ですと/usr/local/etcですが、 これは OS や package によって異なります。 適切なディレクトリを指定してください。

smb.conf と同じディレクトリに、 smb.conf.global.ayame.my.domain というファイルを作成してください。 このファイルに

socket options = TCP_NODELAY SO_SNDBUF=yyyy

という一行を書き込みます。 yyyy は、 先に求めた、mss の値の丁度2倍の値を指定してください。 できの悪い NIC の場合、 1byte でも大きな値を指定すると一気にパフォーマンスが劣化し、 救いがたい状態が発生します。

あとは、smbd を再起動してやれば完成です。


マシンに依存しない、特定の OS 用の設定ファイルは smb.conf.global.<OS name> に、 特定マシンの特定 OS の場合(マルチブートとか)は、 smb.conf.global.<OS name>.<Machine name> というファイルに必要な設定を書いてください。

include ファイル指定行における %M は internet machine name です。

同様に %a は OS name ですが、 どの OS の場合にどのような文字列になるのかは、 smb.conf のマニュアルをみてください。

4. Why does this work?

なぜこれでうまく行くのか。

Winsock2 の動きから、 Winsock2 は IP パケットを2つ受信すると Ack を送ることに集中してしまい、 Ethernet Drive から data を吸い出さなくなり、 結果 NIC あるいは NIC Driver のバッファが溢れて パケットを取りこぼしてしまうのだろう、と推定できます。

NT で取りこぼしが生じないのは、 受信側が Ack を送ることに集中しているとき、 送信側は Ack を受けることに集中していて何も送信していないから、 と推定できるわけです。

そこで、送信側(Samba Server 側) の Send Buffer Size を 丁度 mss * 2 というサイズにして、 送信側の TCP 層が IP Packet 2つ分のデータを送ったら Ack を待つようにしてしまえばよい、 ということが判ります。 結果、Packet の取りこぼしはなくなり最終的な転送速度が向上します。


実は、他の、効率の良い NIC カードの場合でも、 SO_SNDBUF のサイズはmss の倍数である方が効率が良いはずです。 ですので、本当は、8192byteとかを指定するよりは、 mss が 536 byte の場合は 8576 ( 8192/536 は 15.2... ですので、16*536 = 8576 )、 1460 の場合は 8760 ( 8192/1460 は 5.61.. ですので、6*1460 = 8760 ) を指定した方が良い、ということになります。

どちらが来るか自信がない場合は 8576 がいいと思う。 8192 よりは少しだけ効率がいいはずだ。

一般に、mss * n (n は自然数) が SO_SNDBUF で指定するべき適切なサイズです。 1 packet 分でも越えてしまうと、 その瞬間に packet lost が発生して効率が大幅に落ちますので、 どこが限界なのかは簡単に判ります。 いろいろ n の値を変更してみて、限界の値を探ってみると良いでしょう。


あと、この smb.conf がうまく動くのは、 マニュアルに明記されていない、次の2つの性質を利用しています。

  1. include コマンドは実はファイル発見に失敗しても smbd の起動に影響を与えない。 つまり include に失敗するとそのまま smbd の起動が失敗する、 等の現象は起さない。
  2. socket options (に限らないのでしょうが)、 smb.conf ファイルの最後に指定した行だけが有効になる。 従って、include 等で複数のsocket options を指定できても、 最後に指定された1行だけが有効になります。

5. What you have to be care of.

この設定をすると、 ayame.my.domain が nohshi.my.domain をアクセスする速度は、NIC によらずほぼ一定になります。 ですので、後で高性能な NIC を買って Ayame に挿しても、 パフォーマンスは上がりません。 NIC の性能を無視して、少しづつしか送らないのですから。

この場合は、smb.conf.global.ayame.my.domain (あるいは、適宜、正しい設定ファイル)を消す、 変更するなどして、 効率の良いパラメータに変更する必要があります。


change log

2006/01/17
どうやら、この問題、Hubが原因でも起こるらしい。
確かにHubというのも Twisted Pair ケーブルの反対側、というだけの視点で見れば、 NIC と何も変わらないからねぇ。
とはいえ、組み合わせがこれ以上増大するのは勘弁して欲しいなぁ。
2004/08/16
Windows XP でもこの現象が起こる場合がある、との報告をいただいた。 NIC は 3Com 3C90X らしい。 新しい Winsockが問題解決にならなかった理由が知りたい…。
2000/04/18
「速い」とあるべきものが「早い」になっていた。

このページに関する コメント、情報、誤りなどの情報がありましたら、 奥山 まで。