Path: sranha!katsu From: katsu@sra.co.jp (WATANABE Katsuhiro) Message-ID: Date: 17 Dec 90 15:31:11 Organization: Software Research Associates, Inc.,Japan In-reply-to: t-ishii@sra.co.jp's message of 3 Dec 90 09:10:38 GMT Newsgroups: sra.unix Subject: Re: TCP send buffer size Distribution: sra References: SOCK_STREAM type ソケットへの write(2) の成否に関連して、 記事 で t-ishii@sra.co.jp (Tatsuo Ishii) さんいはく > 疑問その1:from write(2) のマニュアル: > フロー制御を前提としたソケットのようなオブジェクトに対して非 > ブロッキング入出力を使用する場合には、 write と writev は、 > 要求よりも少ないバイト数を書き込むことがあります。 > これって、非ブロッキング入出力でなければ、わたなべさんのいうように、 > write() で要求したバイト数分だけ1回で書ける、と読めませんか? > # これ以上は、 tahoe のネットワークコードでも見るしかないのだろうか。 というわけで、見ました。結論だけ言えば、 1.SOCK_STREAM 型のソケットであり、かつ 2.FNDELAY でない状態(普通のブロッキング入出力)であり、かつ 3.シグナルを捕捉した後シグナルハンドラから return することがない ならば、write(2)から返ってきた時の値は、-1 かまたは書き込もうとした 長さになっているはずです。(つまり、半分しか書けないような心配は しなくてよい。) でも実際のコードはわたしには全部理解しきれていないので、 「絶対かー?」とか「命かけるかー?」などと問わないで下さい。 以下に私が理解した範囲での説明を書いてみます。間違っていたら 指摘して下さい。指摘がなければ正しいという合意が形成されたと みなしますよ。:-) ソケットへの write(2) の実装の本体は、sys/uipc_socket.c の中の sosend() という関数です。 (シグナルを別にして)システムコールの呼びだし形態を考えてみると、 この関数から return することは、ソケットへのwrite(2) が終了するための 必要条件になっています。かつ返り値が 0 以外の時は、システムコールが 失敗したものとみなされて、write(2) の返り値は -1 になります。つまり、 送信が完全に成功していないのに sosend() から 0 が返されるようなことが もしあれば、石井さんや齊藤さんが心配していたような事態になるわけです。 SOCK_STREAM に関係するところだけに着目して sosend() のだいたいの 構造をとりだすと、 sosend(so, ..., uio, ...) struct socket *so; struct uio *uio; { int error = 0; struct mbuf *m; restart: do { ロック(so の送り側バッファ); if (so に関して、接続断などのエラーが発生している?) { error = エラーの種類; /* */ goto release; if (so の送り側バッファが少ない?) { /* 「少ない?」はページサイズも考慮した条件 */ ロックはずし(so の送り側バッファ); 資源待ち(so の送り側バッファ); goto restart; } while (so の送り側バッファの大きさ > 0) { 記憶域獲得(m); /* ページングも考えて */ error = カーネル空間へコピー(m, 送りたいデータ); /* buserror, segmentation vioration 等 */ if (error) goto release; if (送りたいデータをコピーしおわった?) break; } if (error) goto release; error = プロトコルルーチンに送信願い(m のリスト, .....); if (error) break; } while (送りたいデータがまだある?) release: ロックはずし(so の送り側バッファ); 記憶域解放(カーネル空間にコピーしてあった送りたいデータ); if (error = EPIPE) psignal(呼び出したプロセス, SIGPIPE); return (error); } のようになっています。前述したとうり、返り値が 0 以外ならば システムコールは失敗したとみなされます。さて、return しているのは 最後の return (error) のところだけで、なおかつ関数内での error の 値は、真のエラーが起きない限り 0 以外になることはありません。 つまり、この関数の返り値が 0(write(2) は送信したサイズを返すで あろう)であるためには、送りたいデータが下位層にすべて送りきられて いる必要があるというわけです。 なお、signal が発生した場合は、資源待ちの所でシステムコールの 始まりのあたりに longjmp してしまって、途中までしかかけなかった ということが起きるのじゃないかと考えています。(sys/sys_generic.c の rwuio() と、sys/kern_synch.c の sleep() 参照) -- ----____----____ 渡邊克宏@ソフトウェア工学研朽所