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

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

このコミットは、Go言語のsyscallパッケージにおいて、sendmsgシステムコールをラップする新しい関数SendmsgNを追加するものです。この新しい関数は、従来のSendmsg関数がエラーのみを返していたのに対し、転送されたバイト数も返すように設計されています。これにより、sendmsgシステムコールの結果をより詳細に、かつ効率的に取得できるようになります。対象となるOSはBSD系(Darwin, Dragonfly, FreeBSD, NetBSD, OpenBSD)、Linux、およびSolarisです。

コミット

commit a7858a40a5bd8481fee839fddd29c305bb4093ef
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Sat Mar 29 09:28:40 2014 +0900

    syscall: add SendmsgN for BSD variants, Linux and Solaris
    
    SendmsgN is an alternate version Sendmsg that also returns
    the number of bytes transferred, instead of just the error.
    
    Update #7645
    
    LGTM=aram, iant
    R=iant, aram, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/81210043

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

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

元コミット内容

syscall: add SendmsgN for BSD variants, Linux and Solaris

このコミットは、SendmsgNという新しい関数をsyscallパッケージに追加します。SendmsgNは、既存のSendmsg関数の代替バージョンであり、転送されたバイト数とエラーの両方を返します。従来のSendmsgはエラーのみを返していました。この変更は、Issue #7645 に関連しています。

変更の背景

従来のsyscall.Sendmsg関数は、Unix系システムコールであるsendmsg(2)をGoから呼び出すためのラッパーでしたが、その戻り値はエラー情報のみでした。しかし、sendmsg(2)システムコール自体は、データの送信が成功した場合に送信されたバイト数を返します。このバイト数は、特にネットワークプログラミングにおいて、実際にどれだけのデータが送信されたかを確認するために非常に重要です。例えば、部分的な送信が発生した場合や、OOB (Out-of-Band) データと通常データが混在する場合など、送信バイト数を正確に把握する必要があるシナリオが存在します。

Issue #7645("syscall: sendmsg should return n")では、このsendmsgシステムコールが返すバイト数をGoのsyscallパッケージからも取得できるようにすべきだという要望が挙げられていました。このコミットは、その要望に応える形で、バイト数を返すSendmsgN関数を導入し、より低レベルかつ詳細な制御を必要とするアプリケーション開発者に対して、必要な情報を提供するものです。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

  • システムコール (System Call): オペレーティングシステム (OS) のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイルI/O、ネットワーク通信、プロセス管理など、OSの機能にアクセスするために使用されます。sendmsgは、ソケットを介してデータを送信するためのシステムコールの一つです。
  • sendmsg(2): Unix系OSにおけるシステムコールで、ソケットを介してメッセージを送信するために使用されます。このシステムコールは、通常のデータだけでなく、補助データ(コントロールメッセージ、例: ファイルディスクリプタの転送など)や、送信先アドレスを一度に指定して送信する機能を提供します。成功時には送信されたバイト数を返します。
  • syscallパッケージ (Go言語): Go言語の標準ライブラリの一つで、低レベルなOSのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムから直接OSの機能を利用できますが、OSに依存するコードとなるため、移植性には注意が必要です。
  • Msghdr構造体: sendmsgシステムコールで使用される構造体で、送信するメッセージに関する情報(データバッファ、補助データバッファ、送信先アドレスなど)をカプセル化します。
  • Sockaddrインターフェース: ソケットアドレスを表すGoのインターフェースです。異なる種類のソケットアドレス(IPv4、IPv6、Unixドメインなど)を抽象化します。
  • unsafe.Pointer: Go言語におけるunsafeパッケージの一部で、任意の型のポインタを表現するために使用されます。Goの型安全性をバイパスするため、慎重な使用が求められます。システムコールのように、C言語のAPIと直接やり取りする際に必要となることがあります。
  • Syscall関数 (Go言語): syscallパッケージ内で、実際のシステムコールを呼び出すための低レベルな関数です。引数としてシステムコール番号と、そのシステムコールに渡す引数をuintptr型で受け取ります。戻り値として、システムコールが返すレジスタの値(通常は戻り値とエラーコード)とエラーを返します。
  • BSD variants (BSD系OS): Berkeley Software Distributionを起源とするUnix系オペレーティングシステムの総称です。FreeBSD, OpenBSD, NetBSD, Dragonfly BSDなどが含まれます。macOS (Darwin) もBSD系カーネルをベースにしています。
  • Linux: オープンソースのUnix系オペレーティングシステムカーネルです。
  • Solaris: Sun Microsystems(現在はOracle)が開発したUnix系オペレーティングシステムです。
  • Issue #7645: GoのIssueトラッカーで管理されている、この変更の背景となった具体的な要望やバグ報告です。この場合、「sendmsgがバイト数を返すようにすべき」という内容でした。

技術的詳細

このコミットの主要な変更点は、syscallパッケージ内のsendmsgシステムコールラッパーの挙動を変更し、送信バイト数を返すようにしたことです。

具体的には、以下の変更が行われました。

  1. sendmsg関数のシグネチャ変更: 各OS(BSD系、Linux、Solaris)のsyscallパッケージ内部で定義されている低レベルなsendmsg関数のシグネチャが、func sendmsg(s int, msg *Msghdr, flags int) (err error)からfunc sendmsg(s int, msg *Msghdr, flags int) (n int, err error)に変更されました。これにより、システムコールが返すバイト数nがGoの関数からも直接取得できるようになります。

  2. SendmsgN関数の追加: 新しい公開関数SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)が追加されました。この関数は、従来のSendmsgと同様の引数を受け取りますが、成功時に送信されたバイト数nを返します。

  3. Sendmsg関数の変更: 既存のSendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error)関数は、内部的に新しく追加されたSendmsgNを呼び出すように変更されました。これにより、Sendmsgの既存の挙動(エラーのみを返す)を維持しつつ、SendmsgNを通じてバイト数取得の機能を提供します。

  4. zsyscall_*.goファイルの更新: zsyscall_*.goファイルは、go generateコマンドによって自動生成されるファイルで、各OSアーキテクチャごとのシステムコールラッパーの実装を含んでいます。このコミットでは、これらのファイル内のsendmsg関数の呼び出し箇所が更新され、システムコールからの戻り値(バイト数)を適切に取得し、nとして返すように修正されました。具体的には、Syscall関数の戻り値r0nに代入する変更が行われています。

  5. エラーハンドリングの修正: SendmsgN関数内で、to.sockaddr()呼び出しでエラーが発生した場合の戻り値がreturn 0, errに変更され、バイト数nがゼロとして初期化されるようになりました。

  6. 特殊なケースの考慮: SendmsgNの実装において、oob(Out-of-Bandデータ)のみが送信され、通常のデータpが空の場合に、nを0に設定するロジックが追加されました。これは、一部のOSやプロトコルにおいて、OOBデータのみの送信がバイト数としてカウントされない場合があるため、その挙動を考慮したものです。

これらの変更により、Goのsyscallパッケージは、sendmsgシステムコールの完全な戻り値(送信バイト数とエラー)をGoプログラムに公開できるようになり、より柔軟で正確なネットワークプログラミングが可能になりました。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイル群に見られます。

  1. src/pkg/syscall/syscall_bsd.go: BSD系OS向けのSendmsgおよびSendmsgNの実装。
  2. src/pkg/syscall/syscall_linux.go: Linux向けのSendmsgおよびSendmsgNの実装。
  3. src/pkg/syscall/syscall_solaris.go: Solaris向けのSendmsgおよびSendmsgNの実装。
  4. src/pkg/syscall/zsyscall_*.go: 各OSおよびアーキテクチャ(例: zsyscall_darwin_amd64.go, zsyscall_linux_386.goなど)ごとの自動生成されたシステムコールラッパーファイル。これらのファイル内のsendmsg関数のシグネチャと、Syscallからの戻り値の処理が変更されています。

以下に、src/pkg/syscall/syscall_bsd.gosrc/pkg/syscall/zsyscall_darwin_amd64.goの変更点を例として示します。他のOS向けのファイルも同様のパターンで変更されています。

src/pkg/syscall/syscall_bsd.go の変更例:

--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -389,15 +389,20 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
 	return
 }
 
-//sys	sendmsg(s int, msg *Msghdr, flags int) (err error)
+//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 
 func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
+\t_, err = SendmsgN(fd, p, oob, to, flags)
+\treturn
+}
+
+func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
 	var ptr unsafe.Pointer
 	var salen _Socklen
 	if to != nil {
 		ptr, salen, err = to.sockaddr()
 		if err != nil {
-\t\t\treturn
+\t\t\treturn 0, err
 		}
 	}
 	var msg Msghdr
@@ -420,10 +425,13 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
 	}
 	msg.Iov = &iov
 	msg.Iovlen = 1
-\tif err = sendmsg(fd, &msg, flags); err != nil {
-\t\treturn
+\tif n, err = sendmsg(fd, &msg, flags); err != nil {
+\t\treturn 0, err
 	}
-\treturn
+\tif len(oob) > 0 && len(p) == 0 {
+\t\tn = 0
+\t}
+\treturn n, nil
 }
 
 //sys	kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, nevent int, timeout *Timespec) (n int, err error)

src/pkg/syscall/zsyscall_darwin_amd64.go の変更例:

--- a/src/pkg/syscall/zsyscall_darwin_amd64.go
+++ b/src/pkg/syscall/zsyscall_darwin_amd64.go
@@ -185,8 +185,9 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func sendmsg(s int, msg *Msghdr, flags int) (err error) {
-\t_, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))\n
+func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+\tr0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))\n
+\tn = int(r0)\n
 \tif e1 != 0 {\n
 \t\terr = e1\n
 \t}\n

コアとなるコードの解説

上記のコード変更は、Goのsyscallパッケージがsendmsgシステムコールをどのように扱うかを根本的に変更しています。

  1. //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error): これはGoのsyscallパッケージがシステムコールをラップする際に使用する特殊なコメントです。このコメントは、go generateツールがzsyscall_*.goのようなファイルを生成する際に、対応するC言語のシステムコール(この場合はsendmsg(2))のシグネチャをGoの関数シグネチャにマッピングするために使用されます。変更前はerr errorのみを返していましたが、変更後はn int, err errorを返すように明示されており、システムコールが返すバイト数nがGoの関数に伝播されるようになります。

  2. func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { ... }: この関数は、Goのユーザーが通常利用する高レベルなラッパーです。変更後、この関数は新しく追加されたSendmsgNを呼び出し、その戻り値のうちエラーのみを返しています。これは、既存のSendmsgのAPI互換性を維持するための措置です。

  3. func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { ... }: この関数が、このコミットで新しく導入された主要なAPIです。

    • var ptr unsafe.Pointervar salen _Socklen: to Sockaddrインターフェースから、実際のソケットアドレス構造体へのポインタと長さを取得するための変数です。unsafe.Pointerは、Goの型システムをバイパスして任意のメモリを指すことができるため、C言語のシステムコールにGoのデータを渡す際に必要となります。
    • if to != nil { ... }: 送信先アドレスが指定されている場合、to.sockaddr()を呼び出してソケットアドレスのポインタと長さを取得します。ここでエラーが発生した場合は、return 0, errとして、バイト数nを0としてエラーを返します。
    • var msg Msghdr: sendmsgシステムコールに渡すMsghdr構造体を初期化します。この構造体には、送信するデータ(p)、補助データ(oob)、送信先アドレスなどが設定されます。
    • msg.Iov = &iovmsg.Iovlen = 1: Msghdr構造体のIovフィールドは、送信するデータバッファの配列(iovec構造体)を指します。ここでは単一のデータバッファ(p)を送信するため、Iovlenは1に設定されます。
    • if n, err = sendmsg(fd, &msg, flags); err != nil { ... }: ここが最も重要な変更点です。低レベルなsendmsg関数を呼び出し、その戻り値であるバイト数nとエラーerrを直接受け取ります。エラーが発生した場合は、return 0, errとして処理を終了します。
    • if len(oob) > 0 && len(p) == 0 { n = 0 }: これは、OOBデータのみが送信され、通常のデータが送信されない場合の特殊なケースを処理します。一部のシステムでは、この場合にsendmsgが返すバイト数が期待通りにならないことがあるため、明示的にnを0に設定しています。
    • return n, nil: 成功した場合、送信されたバイト数nnilエラーを返します。
  4. func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { ... } (in zsyscall_*.go): この自動生成された関数は、GoのSyscall関数を介して実際のOSのsendmsgシステムコールを呼び出します。

    • r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)): Syscall関数は、システムコール番号(SYS_SENDMSG)と引数をuintptr型で受け取ります。戻り値として、レジスタr0(通常はシステムコールの戻り値)、r1(未使用)、およびエラーコードe1を返します。
    • n = int(r0): ここで、システムコールが返したバイト数(r0に格納されている)をint型にキャストしてnに代入しています。これが、バイト数をGoの関数に伝播させるための鍵となる部分です。
    • if e1 != 0 { err = e1 }: システムコールがエラーを返した場合、e1をGoのエラー型に変換してerrに代入します。

これらの変更により、Goのsyscallパッケージは、sendmsgシステムコールのより完全な情報(送信バイト数)をGoプログラムに提供できるようになり、低レベルなネットワーク通信の制御がより柔軟になりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Unix系OSのシステムコールに関するmanページ
  • Go言語のsyscallパッケージのソースコード
  • Go言語のコードレビューシステム (Gerrit)
  • Go言語のIssueトラッカー (GitHub Issues)
  • unsafeパッケージに関するGoのドキュメント
  • iovec構造体に関する情報 (例: man 2 writev)
  • Msghdr構造体に関する情報 (例: man 2 sendmsg)
  • Go言語におけるgo generateの仕組み
  • Go言語におけるクロスプラットフォーム開発の慣習
  • Go言語のsyscallパッケージの設計思想に関する議論
  • Go言語のネットワークプログラミングに関する資料
  • BSD系OS、Linux、Solarisにおけるシステムコールの挙動の違いに関する情報I have generated the detailed technical explanation in Markdown format, including all the required sections and leveraging the commit data and web search for context. I have ensured it is in Japanese and provides maximum detail.

I will now output the generated Markdown to standard output.

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

このコミットは、Go言語の`syscall`パッケージにおいて、`sendmsg`システムコールをラップする新しい関数`SendmsgN`を追加するものです。この新しい関数は、従来の`Sendmsg`関数がエラーのみを返していたのに対し、転送されたバイト数も返すように設計されています。これにより、`sendmsg`システムコールの結果をより詳細に、かつ効率的に取得できるようになります。対象となるOSはBSD系(Darwin, Dragonfly, FreeBSD, NetBSD, OpenBSD)、Linux、およびSolarisです。

## コミット

commit a7858a40a5bd8481fee839fddd29c305bb4093ef Author: Mikio Hara mikioh.mikioh@gmail.com Date: Sat Mar 29 09:28:40 2014 +0900

syscall: add SendmsgN for BSD variants, Linux and Solaris

SendmsgN is an alternate version Sendmsg that also returns
the number of bytes transferred, instead of just the error.

Update #7645

LGTM=aram, iant
R=iant, aram, bradfitz
CC=golang-codereviews
https://golang.org/cl/81210043

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

[https://github.com/golang/go/commit/a7858a40a5bd8481fee839fddd29c305bb4093ef](https://github.com/golang/go/commit/a7858a40a5bd8481fee839fddd29c305bb4093ef)

## 元コミット内容

`syscall: add SendmsgN for BSD variants, Linux and Solaris`

このコミットは、`SendmsgN`という新しい関数を`syscall`パッケージに追加します。`SendmsgN`は、既存の`Sendmsg`関数の代替バージョンであり、転送されたバイト数とエラーの両方を返します。従来の`Sendmsg`はエラーのみを返していました。この変更は、Issue #7645 に関連しています。

## 変更の背景

従来の`syscall.Sendmsg`関数は、Unix系システムコールである`sendmsg(2)`をGoから呼び出すためのラッパーでしたが、その戻り値はエラー情報のみでした。しかし、`sendmsg(2)`システムコール自体は、データの送信が成功した場合に送信されたバイト数を返します。このバイト数は、特にネットワークプログラミングにおいて、実際にどれだけのデータが送信されたかを確認するために非常に重要です。例えば、部分的な送信が発生した場合や、OOB (Out-of-Band) データと通常データが混在する場合など、送信バイト数を正確に把握する必要があるシナリオが存在します。

Issue #7645("syscall: sendmsg should return n")では、この`sendmsg`システムコールが返すバイト数をGoの`syscall`パッケージからも取得できるようにすべきだという要望が挙げられていました。このコミットは、その要望に応える形で、バイト数を返す`SendmsgN`関数を導入し、より低レベルかつ詳細な制御を必要とするアプリケーション開発者に対して、必要な情報を提供するものです。

## 前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

*   **システムコール (System Call)**: オペレーティングシステム (OS) のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイルI/O、ネットワーク通信、プロセス管理など、OSの機能にアクセスするために使用されます。`sendmsg`は、ソケットを介してデータを送信するためのシステムコールの一つです。
*   **`sendmsg(2)`**: Unix系OSにおけるシステムコールで、ソケットを介してメッセージを送信するために使用されます。このシステムコールは、通常のデータだけでなく、補助データ(コントロールメッセージ、例: ファイルディスクリプタの転送など)や、送信先アドレスを一度に指定して送信する機能を提供します。成功時には送信されたバイト数を返します。
*   **`syscall`パッケージ (Go言語)**: Go言語の標準ライブラリの一つで、低レベルなOSのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムから直接OSの機能を利用できますが、OSに依存するコードとなるため、移植性には注意が必要です。
*   **`Msghdr`構造体**: `sendmsg`システムコールで使用される構造体で、送信するメッセージに関する情報(データバッファ、補助データバッファ、送信先アドレスなど)をカプセル化します。
*   **`Sockaddr`インターフェース**: ソケットアドレスを表すGoのインターフェースです。異なる種類のソケットアドレス(IPv4、IPv6、Unixドメインなど)を抽象化します。
*   **`unsafe.Pointer`**: Go言語における`unsafe`パッケージの一部で、任意の型のポインタを表現するために使用されます。Goの型安全性をバイパスするため、慎重な使用が求められます。システムコールのように、C言語のAPIと直接やり取りする際に必要となることがあります。
*   **`Syscall`関数 (Go言語)**: `syscall`パッケージ内で、実際のシステムコールを呼び出すための低レベルな関数です。引数としてシステムコール番号と、そのシステムコールに渡す引数を`uintptr`型で受け取ります。戻り値として、システムコールが返すレジスタの値(通常は戻り値とエラーコード)とエラーを返します。
*   **BSD variants (BSD系OS)**: Berkeley Software Distributionを起源とするUnix系オペレーティングシステムの総称です。FreeBSD, OpenBSD, NetBSD, Dragonfly BSDなどが含まれます。macOS (Darwin) もBSD系カーネルをベースにしています。
*   **Linux**: オープンソースのUnix系オペレーティングシステムカーネルです。
*   **Solaris**: Sun Microsystems(現在はOracle)が開発したUnix系オペレーティングシステムです。
*   **Issue #7645**: GoのIssueトラッカーで管理されている、この変更の背景となった具体的な要望やバグ報告です。この場合、「`sendmsg`がバイト数を返すようにすべき」という内容でした。

## 技術的詳細

このコミットの主要な変更点は、`syscall`パッケージ内の`sendmsg`システムコールラッパーの挙動を変更し、送信バイト数を返すようにしたことです。

具体的には、以下の変更が行われました。

1.  **`sendmsg`関数のシグネチャ変更**:
    各OS(BSD系、Linux、Solaris)の`syscall`パッケージ内部で定義されている低レベルな`sendmsg`関数のシグネチャが、`func sendmsg(s int, msg *Msghdr, flags int) (err error)`から`func sendmsg(s int, msg *Msghdr, flags int) (n int, err error)`に変更されました。これにより、システムコールが返すバイト数`n`がGoの関数からも直接取得できるようになります。

2.  **`SendmsgN`関数の追加**:
    新しい公開関数`SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)`が追加されました。この関数は、従来の`Sendmsg`と同様の引数を受け取りますが、成功時に送信されたバイト数`n`を返します。

3.  **`Sendmsg`関数の変更**:
    既存の`Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error)`関数は、内部的に新しく追加された`SendmsgN`を呼び出すように変更されました。これにより、`Sendmsg`の既存の挙動(エラーのみを返す)を維持しつつ、`SendmsgN`を通じてバイト数取得の機能を提供します。

4.  **`zsyscall_*.go`ファイルの更新**:
    `zsyscall_*.go`ファイルは、`go generate`コマンドによって自動生成されるファイルで、各OSアーキテクチャごとのシステムコールラッパーの実装を含んでいます。このコミットでは、これらのファイル内の`sendmsg`関数の呼び出し箇所が更新され、システムコールからの戻り値(バイト数)を適切に取得し、`n`として返すように修正されました。具体的には、`Syscall`関数の戻り値`r0`を`n`に代入する変更が行われています。

5.  **エラーハンドリングの修正**:
    `SendmsgN`関数内で、`to.sockaddr()`呼び出しでエラーが発生した場合の戻り値が`return 0, err`に変更され、バイト数`n`がゼロとして初期化されるようになりました。

6.  **特殊なケースの考慮**:
    `SendmsgN`の実装において、`oob`(Out-of-Bandデータ)のみが送信され、通常のデータ`p`が空の場合に、`n`を0に設定するロジックが追加されました。これは、一部のOSやプロトコルにおいて、OOBデータのみの送信がバイト数としてカウントされない場合があるため、その挙動を考慮したものです。

これらの変更により、Goの`syscall`パッケージは、`sendmsg`システムコールの完全な戻り値(送信バイト数とエラー)をGoプログラムに公開できるようになり、より柔軟で正確なネットワークプログラミングが可能になりました。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイル群に見られます。

1.  **`src/pkg/syscall/syscall_bsd.go`**: BSD系OS向けの`Sendmsg`および`SendmsgN`の実装。
2.  **`src/pkg/syscall/syscall_linux.go`**: Linux向けの`Sendmsg`および`SendmsgN`の実装。
3.  **`src/pkg/syscall/syscall_solaris.go`**: Solaris向けの`Sendmsg`および`SendmsgN`の実装。
4.  **`src/pkg/syscall/zsyscall_*.go`**: 各OSおよびアーキテクチャ(例: `zsyscall_darwin_amd64.go`, `zsyscall_linux_386.go`など)ごとの自動生成されたシステムコールラッパーファイル。これらのファイル内の`sendmsg`関数のシグネチャと、`Syscall`からの戻り値の処理が変更されています。

以下に、`src/pkg/syscall/syscall_bsd.go`と`src/pkg/syscall/zsyscall_darwin_amd64.go`の変更点を例として示します。他のOS向けのファイルも同様のパターンで変更されています。

**`src/pkg/syscall/syscall_bsd.go` の変更例:**

```diff
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -389,15 +389,20 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
 	return
 }
 
-//sys	sendmsg(s int, msg *Msghdr, flags int) (err error)
+//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 
 func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
+\t_, err = SendmsgN(fd, p, oob, to, flags)
+\treturn
+}
+
+func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
 	var ptr unsafe.Pointer
 	var salen _Socklen
 	if to != nil {
 		ptr, salen, err = to.sockaddr()
 		if err != nil {
-\t\t\treturn
+\t\t\treturn 0, err
 		}
 	}
 	var msg Msghdr
@@ -420,10 +425,13 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
 	}
 	msg.Iov = &iov
 	msg.Iovlen = 1
-\tif err = sendmsg(fd, &msg, flags); err != nil {
-\t\treturn
+\tif n, err = sendmsg(fd, &msg, flags); err != nil {
+\t\treturn 0, err
 	}
-\treturn
+\tif len(oob) > 0 && len(p) == 0 {
+\t\tn = 0
+\t}\n+\treturn n, nil
 }
 
 //sys	kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, nevent int, timeout *Timespec) (n int, err error)

src/pkg/syscall/zsyscall_darwin_amd64.go の変更例:

--- a/src/pkg/syscall/zsyscall_darwin_amd64.go
+++ b/src/pkg/syscall/zsyscall_darwin_amd64.go
@@ -185,8 +185,9 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func sendmsg(s int, msg *Msghdr, flags int) (err error) {
-\t_, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))\n
+func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+\tr0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))\n
+\tn = int(r0)\n
 \tif e1 != 0 {\n
 \t\terr = e1\n
 \t}\n

コアとなるコードの解説

上記のコード変更は、Goのsyscallパッケージがsendmsgシステムコールをどのように扱うかを根本的に変更しています。

  1. //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error): これはGoのsyscallパッケージがシステムコールをラップする際に使用する特殊なコメントです。このコメントは、go generateツールがzsyscall_*.goのようなファイルを生成する際に、対応するC言語のシステムコール(この場合はsendmsg(2))のシグネチャをGoの関数シグネチャにマッピングするために使用されます。変更前はerr errorのみを返していましたが、変更後はn int, err errorを返すように明示されており、システムコールが返すバイト数nがGoの関数に伝播されるようになります。

  2. func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { ... }: この関数は、Goのユーザーが通常利用する高レベルなラッパーです。変更後、この関数は新しく追加されたSendmsgNを呼び出し、その戻り値のうちエラーのみを返しています。これは、既存のSendmsgのAPI互換性を維持するための措置です。

  3. func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { ... }: この関数が、このコミットで新しく導入された主要なAPIです。

    • var ptr unsafe.Pointervar salen _Socklen: to Sockaddrインターフェースから、実際のソケットアドレス構造体へのポインタと長さを取得するための変数です。unsafe.Pointerは、Goの型システムをバイパスして任意のメモリを指すことができるため、C言語のシステムコールにGoのデータを渡す際に必要となります。
    • if to != nil { ... }: 送信先アドレスが指定されている場合、to.sockaddr()を呼び出してソケットアドレスのポインタと長さを取得します。ここでエラーが発生した場合は、return 0, errとして、バイト数nを0としてエラーを返します。
    • var msg Msghdr: sendmsgシステムコールに渡すMsghdr構造体を初期化します。この構造体には、送信するデータ(p)、補助データ(oob)、送信先アドレスなどが設定されます。
    • msg.Iov = &iovmsg.Iovlen = 1: Msghdr構造体のIovフィールドは、送信するデータバッファの配列(iovec構造体)を指します。ここでは単一のデータバッファ(p)を送信するため、Iovlenは1に設定されます。
    • if n, err = sendmsg(fd, &msg, flags); err != nil { ... }: ここが最も重要な変更点です。低レベルなsendmsg関数を呼び出し、その戻り値であるバイト数nとエラーerrを直接受け取ります。エラーが発生した場合は、return 0, errとして処理を終了します。
    • if len(oob) > 0 && len(p) == 0 { n = 0 }: これは、OOBデータのみが送信され、通常のデータが送信されない場合の特殊なケースを処理します。一部のシステムでは、この場合にsendmsgが返すバイト数が期待通りにならないことがあるため、明示的にnを0に設定しています。
    • return n, nil: 成功した場合、送信されたバイト数nnilエラーを返します。
  4. func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { ... } (in zsyscall_*.go): この自動生成された関数は、GoのSyscall関数を介して実際のOSのsendmsgシステムコールを呼び出します。

    • r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)): Syscall関数は、システムコール番号(SYS_SENDMSG)と引数をuintptr型で受け取ります。戻り値として、レジスタr0(通常はシステムコールの戻り値)、r1(未使用)、およびエラーコードe1を返します。
    • n = int(r0): ここで、システムコールが返したバイト数(r0に格納されている)をint型にキャストしてnに代入しています。これが、バイト数をGoの関数に伝播させるための鍵となる部分です。
    • if e1 != 0 { err = e1 }: システムコールがエラーを返した場合、e1をGoのエラー型に変換してerrに代入します。

これらの変更により、Goのsyscallパッケージは、sendmsgシステムコールのより完全な情報(送信バイト数)をGoプログラムに提供できるようになり、低レベルなネットワーク通信の制御がより柔軟になりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Unix系OSのシステムコールに関するmanページ
  • Go言語のsyscallパッケージのソースコード
  • Go言語のコードレビューシステム (Gerrit)
  • Go言語のIssueトラッカー (GitHub Issues)
  • unsafeパッケージに関するGoのドキュメント
  • iovec構造体に関する情報 (例: man 2 writev)
  • Msghdr構造体に関する情報 (例: man 2 sendmsg)
  • Go言語におけるgo generateの仕組み
  • Go言語におけるクロスプラットフォーム開発の慣習
  • Go言語のsyscallパッケージの設計思想に関する議論
  • Go言語のネットワークプログラミングに関する資料
  • BSD系OS、Linux、Solarisにおけるシステムコールの挙動の違いに関する情報