Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13411] ファイルの概要

このコミットは、Go言語の標準ライブラリである net パッケージにおけるFreeBSD環境でのビルド問題を修正するものです。具体的には、sendfile システムコールの呼び出し方に関するFreeBSD特有の要件に対応し、ビルドエラーを解消しています。

コミット

  • コミットハッシュ: f009fefd7977354b4800dc7a242d5c04f50c9778
  • Author: Russ Cox rsc@golang.org
  • Date: Wed Jun 27 17:02:39 2012 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f009fefd7977354b4800dc7a242d5c04f50c9778

元コミット内容

net: fix build (FreeBSD sendfile)

TBR=bradfitz
CC=golang-dev
https://golang.org/cl/6358043

変更の背景

このコミットは、Go言語の net パッケージがFreeBSD上でビルドできない問題を解決するために行われました。問題の原因は、FreeBSDの sendfile システムコールの引数に関する特定の要件が、Goの syscall パッケージからの呼び出しで満たされていなかったことにあります。

sendfile システムコールは、ファイルディスクリプタ間でデータを直接転送することで、ユーザー空間を経由するコピーを避けて効率的なデータ転送を実現するためのものです。Webサーバーなどで静的ファイルを配信する際などにパフォーマンス向上に寄与します。しかし、オペレーティングシステム(OS)によって sendfile のシグネチャ(引数の型や順序)が異なることがあり、Goの syscall パッケージはその差異を吸収する必要があります。

FreeBSDの sendfile システムコールは、ファイル内のオフセット(pos)をポインタで渡すことを要求します。これは、sendfile の実行中にオフセットが更新される可能性があるためです。Goのコードでは、この pos を値として渡していたため、コンパイルエラーが発生していました。このコミットは、このFreeBSD特有の sendfile の挙動に合わせて、pos をポインタとして渡すように修正することで、ビルドエラーを解消し、FreeBSD環境での net パッケージの正常な動作を保証します。

前提知識の解説

sendfile システムコール

sendfile は、カーネル空間内でファイルディスクリプタ間でデータを直接転送するためのシステムコールです。これにより、アプリケーションがデータをユーザー空間に読み込み、その後ソケットに書き込むという二段階のプロセスを回避できます。この「ゼロコピー」の特性により、CPUオーバーヘッドとメモリコピーの回数が削減され、特に大容量ファイルの転送において高いパフォーマンスを発揮します。

sendfile の一般的なシグネチャは以下のようになりますが、OSによって引数の詳細が異なります。

  • Linux: ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
    • offset はポインタで、sendfile の実行中に更新されます。
  • FreeBSD: int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags);
    • offset は値で渡されますが、sbytes (転送されたバイト数) はポインタで渡され、sendfile の実行中に更新されます。
    • Goの syscall.Sendfile は、FreeBSDの sendfileoffset 引数をポインタとして扱うようにラップしています。これは、Goの syscall パッケージがOS間の差異を吸収し、より統一されたインターフェースを提供するためです。

Go言語の syscall パッケージ

Go言語の syscall パッケージは、低レベルのOSプリミティブ(システムコール)へのアクセスを提供します。これにより、Goプログラムはファイル操作、ネットワーク通信、プロセス管理など、OSが提供する基本的な機能と直接対話できます。syscall パッケージはOSごとに異なるシステムコールのシグネチャやセマンティクスを抽象化し、Goのコードからは比較的統一された方法でシステムコールを呼び出せるように設計されています。しかし、完全に抽象化しきれない部分や、特定のOSの挙動に起因する問題が発生することもあります。

Go言語の net パッケージ

Go言語の net パッケージは、ネットワークI/Oのプリミティブインターフェースを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルをサポートしており、クライアントおよびサーバーアプリケーションの構築に利用されます。net パッケージは、内部的に syscall パッケージを利用してOSのネットワーク関連システムコールを呼び出しています。sendfile のような効率的なファイル転送メカニズムも、この net パッケージ内で利用されることがあります。

技術的詳細

この修正は、FreeBSDにおける syscall.Sendfile の呼び出し方に関するものです。FreeBSDの sendfile システムコールは、ファイルオフセットを更新するためにポインタを必要とします。Goの syscall パッケージは、このFreeBSDの sendfile をラップする際に、オフセット引数をポインタとして受け取るように設計されています。

元のコードでは、syscall.Sendfile の第3引数(オフセット)に pos という int64 型の変数を直接渡していました。しかし、syscall.Sendfile のFreeBSD実装は、この引数を *int64 (つまり int64 へのポインタ) として期待しています。そのため、型ミスマッチが発生し、ビルドエラーとなっていました。

このコミットでは、pos の値を一時変数 pos1 にコピーし、その pos1 のアドレス (&pos1) を syscall.Sendfile に渡すように変更しています。これにより、syscall.Sendfilepos1 の指すメモリ位置を更新できるようになり、FreeBSDの sendfile の要件を満たし、ビルドが成功するようになります。

この変更は、Goの syscall パッケージがOS固有のシステムコールをラップする際の、引数の型とセマンティクスに関する厳密な対応の重要性を示しています。特に、C言語のシステムコールがポインタを介して値を変更するような場合、Go側でもそのポインタセマンティクスを正しく反映させる必要があります。

コアとなるコードの変更箇所

diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go
index 7ec6f7ff7e..40afdee96d 100644
--- a/src/pkg/net/sendfile_freebsd.go
+++ b/src/pkg/net/sendfile_freebsd.go
@@ -72,7 +72,8 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
 		if int64(n) > remain {
 			n = int(remain)
 		}
-		n, err1 := syscall.Sendfile(dst, src, pos, n)
+		pos1 := pos
+		n, err1 := syscall.Sendfile(dst, src, &pos1, n)
 		if n > 0 {
 			pos += int64(n)
 			written += int64(n)

コアとなるコードの解説

変更は src/pkg/net/sendfile_freebsd.go ファイル内の sendFile 関数にあります。

  • 変更前:

    n, err1 := syscall.Sendfile(dst, src, pos, n)
    

    ここでは、syscall.Sendfile の第3引数に pos (型は int64) が直接渡されています。FreeBSDの syscall.Sendfile の内部実装は、この引数を *int64 (ポインタ) として期待しているため、コンパイル時に型エラーが発生していました。

  • 変更後:

    pos1 := pos
    n, err1 := syscall.Sendfile(dst, src, &pos1, n)
    
    1. pos1 := pos: まず、現在のオフセット値 pos を新しいローカル変数 pos1 にコピーします。これは、posint64 型の変数であり、その値を直接変更するのではなく、その値のコピーを sendfile に渡すためです。
    2. &pos1: pos1 のアドレス(ポインタ)を取得し、これを syscall.Sendfile の第3引数として渡します。これにより、syscall.Sendfilepos1 が指すメモリ位置に直接書き込むことができ、sendfile システムコールがオフセットを更新する挙動に正しく対応できます。

この修正により、FreeBSD環境での net パッケージのビルドが成功し、sendfile を利用した効率的なファイル転送機能が正しく動作するようになりました。

関連リンク

参考にした情報源リンク