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

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

このコミットは、Go言語のsyscallパッケージにおいて、Native Client (NaCl) 環境向けにSendmsgN関数を追加し、既存のSendmsg関数を更新するものです。これにより、ソケットへのメッセージ送信時に送信バイト数を正確に取得できるようになります。

コミット

commit d873e642cd78cd4943157bcef7cc27f8f4e9d3cc
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon Apr 28 11:34:52 2014 +0900

    syscall: add missing SendmsgN for NaCl
    
    Update #7645
    
    LGTM=bradfitz
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/98790044

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

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

元コミット内容

syscall: add missing SendmsgN for NaCl

Update #7645

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/98790044

変更の背景

このコミットは、Go言語のsyscallパッケージがGoogle Native Client (NaCl) 環境で動作する際の不足を補うものです。具体的には、ソケットにデータを送信する際に、送信されたバイト数を正確に返すSendmsgN関数がNaCl環境向けに欠落していたため、これを追加することが目的です。コミットメッセージにある「Update #7645」は、この機能追加が特定の内部課題(おそらくバグ報告や機能要望)に対応するものであることを示唆しています。

従来のSendmsg関数はエラーのみを返していましたが、ネットワークプログラミングにおいては、実際に何バイトのデータが送信されたかを知ることが重要です。特に、部分的な送信が発生した場合や、送信バッファの管理を行う場合には、この情報が不可欠となります。NaCl環境におけるGoプログラムのネットワーク機能の完全性と堅牢性を向上させるために、この変更が導入されました。

前提知識の解説

Go言語のsyscallパッケージ

syscallパッケージは、Goプログラムからオペレーティングシステム(OS)のシステムコールを直接呼び出すための低レベルなインターフェースを提供します。システムコールは、ファイル操作、ネットワーク通信、プロセス管理など、OSカーネルが提供する基本的なサービスを利用するための手段です。Go言語の標準ライブラリの多くは、このsyscallパッケージを介してOSの機能を利用しています。

Native Client (NaCl)

Native Client (NaCl) は、Googleが開発したサンドボックス技術で、Webブラウザ内でネイティブコード(C/C++など)を安全かつポータブルに実行することを可能にします。NaClは、セキュリティモデルを強化し、悪意のあるコードがシステムにアクセスするのを防ぎながら、ネイティブアプリケーションに近いパフォーマンスを提供することを目指していました。Go言語もNaCl環境をターゲットプラットフォームの一つとしてサポートしており、GoプログラムをNaClモジュールとしてコンパイルし、Webアプリケーション内で実行することができました。ただし、NaClは現在では非推奨(deprecated)となっており、WebAssembly (Wasm) などの新しい技術に置き換えられています。

sendmsgシステムコール

sendmsgは、Unix系OSで利用される汎用的なソケット通信のためのシステムコールです。このシステムコールは、通常のデータだけでなく、補助データ(例: ファイルディスクリプタ、ソケット認証情報など)も同時に送信できる柔軟性を持っています。ネットワークプログラミングにおいて、複雑なメッセージ構造や特殊な制御情報を送る際に利用されます。

SOCK_STREAMSOCK_DGRAM

ソケットプログラミングにおいて、ソケットの種類は通信の特性を決定します。

  • SOCK_STREAM: ストリームソケットは、TCP (Transmission Control Protocol) のように、信頼性があり、コネクション指向で、バイトストリームを提供するソケットです。データの順序が保証され、欠損や重複がありません。
  • SOCK_DGRAM: データグラムソケットは、UDP (User Datagram Protocol) のように、コネクションレスで、信頼性のないデータグラムを提供するソケットです。データの順序は保証されず、欠損や重複が発生する可能性がありますが、オーバーヘッドが少なく高速です。

技術的詳細

このコミットの主要な目的は、NaCl環境におけるsyscall.Sendmsgの動作を改善し、送信バイト数を返すSendmsgN関数を導入することです。

Go言語のsyscallパッケージにおけるネットワーク関連の関数は、OSのシステムコールをラップしています。sendmsgシステムコールは通常、送信されたバイト数を返しますが、Goのsyscall.Sendmsgは歴史的にエラーのみを返す設計になっていました。これは、多くのケースで送信が成功したかどうかが重要であり、送信バイト数はアプリケーションレベルで別途管理されることが多かったためと考えられます。

しかし、特定のシナリオ、特にNaClのようなサンドボックス環境では、より詳細な情報(送信バイト数)が必要となる場合があります。このコミットでは、SendmsgNという新しい関数を導入し、この関数が送信バイト数とエラーの両方を返すように設計されています。既存のSendmsg関数は、この新しいSendmsgNを内部的に呼び出し、その戻り値のうちエラーのみを返すように変更されています。これにより、既存のAPIの互換性を保ちつつ、より詳細な情報を提供する新しいAPIが追加されました。

また、SendmsgNの実装では、ソケットのタイプ(SOCK_STREAMSOCK_DGRAMか)に応じて異なる内部関数(f.writeまたはf.sendto)を呼び分けています。これは、ストリームソケットとデータグラムソケットでデータの送信方法が異なるためです。ストリームソケットではバイトストリームとしてデータを書き込み、データグラムソケットではデータグラムとしてデータを送信します。

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

変更はsrc/pkg/syscall/net_nacl.goファイルに集中しています。

--- a/src/pkg/syscall/net_nacl.go
+++ b/src/pkg/syscall/net_nacl.go
@@ -808,11 +808,26 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn, recvflags int, from Soc
 }
 
 func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) error {
+	_, err := SendmsgN(fd, p, oob, to, flags)
+	return err
+}
+
+func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
 	f, err := fdToNetFile(fd)
 	if err != nil {
-		return err
+		return 0, err
 	}
-	return f.sendto(p, flags, to)
+	switch f.sotype {
+	case SOCK_STREAM:
+		n, err = f.write(p)
+	case SOCK_DGRAM:
+		n = len(p)
+		err = f.sendto(p, flags, to)
+	}
+	if err != nil {
+		return 0, err
+	}
+	return n, nil
 }
 
 func GetsockoptInt(fd, level, opt int) (value int, err error) {

コアとなるコードの解説

Sendmsg関数の変更

既存のSendmsg関数は、以下のように変更されました。

func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) error {
	_, err := SendmsgN(fd, p, oob, to, flags)
	return err
}

この変更により、Sendmsgは新しく追加されたSendmsgN関数を呼び出し、その戻り値のうちエラーのみを返すようになりました。これは、既存のAPIのシグネチャ(引数と戻り値の型)を変更せずに、内部実装を新しいSendmsgNに委譲するための一般的なパターンです。

SendmsgN関数の追加

新しく追加されたSendmsgN関数は以下のようになっています。

func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
	f, err := fdToNetFile(fd)
	if err != nil {
		return 0, err
	}
	switch f.sotype {
	case SOCK_STREAM:
		n, err = f.write(p)
	case SOCK_DGRAM:
		n = len(p)
		err = f.sendto(p, flags, to)
	}
	if err != nil {
		return 0, err
	}
	return n, nil
}
  1. fdToNetFile(fd): まず、与えられたファイルディスクリプタfdから、内部的なnetFile構造体を取得します。このnetFileは、ソケットに関する詳細な情報(ソケットタイプなど)を保持しています。エラーが発生した場合は、直ちに0バイトとエラーを返します。
  2. ソケットタイプの判定: f.sotype(ソケットタイプ)に基づいて、処理を分岐します。
    • SOCK_STREAM (TCP): ストリームソケットの場合、f.write(p)を呼び出します。writeメソッドは、バイトストリームとしてデータをソケットに書き込み、書き込まれたバイト数とエラーを返します。
    • SOCK_DGRAM (UDP): データグラムソケットの場合、n = len(p)で送信バイト数をペイロードの長さとして設定し、f.sendto(p, flags, to)を呼び出します。sendtoメソッドは、指定されたアドレスにデータグラムを送信します。データグラム通信では、通常、送信されるデータの長さは事前に分かっているため、len(p)がそのまま送信バイト数となります。
  3. エラーハンドリング: f.writeまたはf.sendtoの呼び出し後にエラーが発生した場合、0バイトとエラーを返します。
  4. 戻り値: すべての処理が成功した場合、実際に送信されたバイト数nnilエラーを返します。

この変更により、NaCl環境においても、Goプログラムがソケット通信の際に送信バイト数を正確に把握できるようになり、より堅牢なネットワークアプリケーションの開発が可能になります。

関連リンク

参考にした情報源リンク

  • Go言語のsyscallパッケージに関する公式ドキュメント
  • Native Client (NaCl) に関するGoogle Developersのドキュメント(現在は非推奨に関する情報を含む)
  • Unix系OSにおけるsendmsgシステムコールに関するmanページや関連ドキュメント
  • TCP/UDPソケットプログラミングに関する一般的な情報源