Q. LAN 経由での FreeBSD から Windows へのデータ転送が、その逆に比べ 圧倒的に遅いです。Windows 同士だとこのような現象は起りません。 A. 一般的な解は、一般的な要因に依存するため、何とも判りませんが、次 のような場合が Winsock2 の場合にあり得ることは判っています。 一部の NIC では、NIC のせいなのか NIC Driver のせいなのかは不明で すが、Windows で使った場合に、時々 packet の取りこぼしが発生します。 これは、NIC が用意している Recieve Buffer の大きさが、Winsock2 の Recieve Buffer よりも小さい場合、Winsock2 が NIC から packet を 吸い出す前に NIC の Recieve Buffer が溢れてしまうために起るのでは ないか、と推測されます。 Winsock2 の挙動を tcpdump などで見ると判るのですが、Windows 同士の 通信において、Winsock は送信において次のようなルールに従っているよ うに見えます(これは、そう「見える」と言っているだけであって、そう 「なっている」という意味で言っているのではありません。Winsock2 の 実装がどうなっているのかは、そのソースコードを見ることができない以 上、全く不明です)。 I) 2つのマシン A,B があって、A->B というデータ転送しかない場合、 A というマシンは Send, Send, Ack 待ち というパターンを忠実に守っているように見える。 II) 2つのマシン A,B があって、双方向に転送すべきデータがある場合、 A というマシンは Send(これには直前の Send に対する Ack を含んでいる), Data 待ち(これには直前の Send に対する Ack が含まれている) を繰り返す、というパターンを忠実に守っているように見える。 III) I と II を切り替える際には 0.1 sec の wait が入る。 もし、Windows 側の NIC のせいでこの現象が起っている場合は、 Windows->FreeBSD 方向の通信には、この障害は出ていないはずです。 (別の障害は出ているかも知れませんが :) さて、問題の解決方法ですが、ようするに上記の動きを FreeBSD 側で真 似てやれば問題は解決します。具体的には TCP/IP の Send Buffer Size をどうにかして「IP Packet 2つ分」まで減らしてやれば、問題は解決す るはずなのです。そうすれば Winsock2 がそれを全て取り込み、Ack を返 してくるまで先には進めません。 「IP Packet 2つ分」が何バイトなのかを知るには、基本的には tcpdump を使うのが手だと思います。tcpdump は root であれば FreeBSD で使う ことができますし、 WinDump <URL:http://windump.polito.it/> を使えば Windows 側で tcpdump を取ることもできます。これらを使って、 mss の値を獲得します。これが「IP Packet 1つ分」の大きさだと思って ください。これを2倍すれば目的の値になります。 あるいは、ethernet を使っている場合は、mss は大抵、536byte か 1460byte のどちらかです。ですので、その丁度2倍 1072byte かあるいは 2920byte が「IP Packet 2つ分だ」という事ができます。ですので、 2920 を使ってみて、駄目だったら 1072 を使ってみる、という手もあり ます。 で、ここから先は FreeBSD の管理戦略にかなり依存します。 1) システム全体を特定の Windows マシンに合わせる、という場合: この場合、 sysctl コマンドを使うと良いでしょう。root になって $ sysctl -w net.inet.tcp.sendspace=xxxx (xxxx は上記の「IP Packet 2つ分」の大きさ) を設定してみましょう。 多分これでパフォーマンスは向上するはずです。 ちなみに、 $ sysctl -w net.inet.tcp.delayed_ack=0 を設定すると Windows -> FreeBSD 方向の通信も改善される場合がある、 という話があるそうです。 2) Windows マシン毎に設定を変える場合: 残念ながらマシン毎に FreeBSD の設定を変える場合、1 の場合のような 汎用的戦略はありません。各アプリケーションが、それぞれ、自分が通信 している相手を認識し、その適切な値をどこかのテーブルから lookup し なくてはいけませんが、そのようなサポートのないソフトもあるからです。 もし、Samba に関して、というのであれば、smb.conf の中の [global] section の最後に include = /usr/local/etc/smb.conf.global.%m のような1行を加えておき、 /usr/local/etc/smb.conf.global.<その問題の Windows マシン名> というファイル中で socket options = SO_SNDBUF=xxxx TCP_NODELAY (xxxx は上記の「IP Packet 2つ分」の大きさ) を設定するとよいでしょう。上記の include 文は失敗しても smbd の 起動に影響はありません。従って、特に特別な設定をする必要がない target に対しては /usr/local/etc/smb.conf.global.<その問題の Windows マシン名> というファイルを作成する必要はありません。このあたりは <URL:http://www.dd.iij4u.or.jp/~okuyamak/Documents/tuning.japanese.html> を参考にして下さい。 この方法は Winsock2 についてしかチェックできていません。従って、 Winsock1.1 の場合、あるいは将来できるであろう、Winsock2.1 (あるいは Winsock3) でうまく行くのかどうかは判りません。 もし、現在すでにある程度パフォーマンスが良い場合、SO_SNDBUF の値を mss の整数倍にすることで、効率が向上する可能性があります。ですので、 パフォーマンスを重視するマシンに対しては、そのマシン専用の /usr/local/etc/smb.conf.global.<その問題の Windows マシン名> ファイルを作った方がいいかも知れません。 smb.conf には %a(Architecture) というオプションがあります。マルチ ブートマシンがある場合は smb.conf を include = /usr/local/etc/smb.conf.global.%m include = /usr/local/etc/smb.conf.global.%a.%M のように変更して、%a と %M で Client OS と Machine Name を指定して やると各 OS ごとの設定も可能になります。
グループ名: winsock