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

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

このコミットは、GoランタイムのPlan 9ビルドにおける問題を修正するものです。具体的には、runtime·SysUsed関数の引数型がuintptrではなくuintptr nbytesであるべきだったというバグを修正しています。これにより、Plan 9環境でのGoプログラムのメモリ管理が正しく機能するようになります。

コミット

commit 67c79da857671faa65e110f58667c2282403046c
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Aug 15 14:24:28 2013 +0400

    runtime: fix plan9 build
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/12986043

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

https://github.com/golang/go/commit/67c79da857671faa65e110f58667c2282403046c

元コミット内容

runtime: fix plan9 build

変更の背景

この変更は、GoランタイムのPlan 9ビルドが正しく動作しないという問題を解決するために行われました。Go言語は、その設計思想やツールチェインにおいて、Bell Labsで開発されたオペレーティングシステムであるPlan 9から大きな影響を受けています。Goの主要な設計者であるKen ThompsonとRob Pikeは、Plan 9の開発にも携わっていました。そのため、GoはPlan 9に対する実験的なポートを持っており、特定のアーキテクチャ(386, amd64, arm)で動作します。

Goランタイムは、オペレーティングシステムと連携してメモリを管理します。SysUsed関数は、Goランタイムがオペレーティングシステムから取得したメモリの総量を表し、そのメモリがGoプロセスによって「使用中」であることを示します。この関数は、Goのガベージコレクタがメモリを解放した後、そのメモリ領域がもはや必要ないとOSに通知するために使用されるSysUnusedと対をなすものです。

問題は、runtime/mem_plan9.cファイル内のruntime·SysUsed関数のシグネチャが、期待されるuintptr nbytesではなく、単にuintptr nとなっていたことです。この引数名の不一致が、Plan 9環境でのビルドエラーや不正な動作を引き起こしていたと考えられます。Goのコンパイラやリンカは、関数シグネチャの厳密な一致を要求するため、このような不一致はビルドの失敗に直結します。

前提知識の解説

Goランタイムのメモリ管理 (SysUsed, SysUnused)

Go言語は、自動ガベージコレクション(GC)によってメモリ管理を行います。しかし、GCはあくまでGoランタイム内部でのオブジェクトのライフサイクル管理であり、実際にオペレーティングシステムからメモリを確保したり解放したりする低レベルな処理は、GoランタイムがOSのシステムコールを介して行います。

  • SysUsed: Goランタイムがオペレーティングシステムから取得し、現在使用している(または予約している)メモリの総量を指します。これは、GoプロセスがOSから見てどれだけのメモリを占有しているかを示す指標です。Goのヒープメモリや、ランタイムが内部的に使用するデータ構造などが含まれます。
  • SysUnused: SysUsedで確保されたメモリのうち、GoランタイムがもはやGoオブジェクトを保持するために必要としないと判断した部分を指します。ガベージコレクタがメモリを解放した後、GoランタイムはSysUnusedを呼び出して、その物理メモリページがOSによって再利用可能であることを通知します。ただし、Goランタイムは仮想アドレス空間を保持し続けることがあり、これは将来のメモリ割り当てのためにシステムコールを減らすための最適化です。

これらの関数は、GoランタイムがOSと効率的にメモリをやり取りするための重要なインターフェースです。

uintptr

Go言語のuintptr型は、符号なし整数型であり、任意のポインタのビットパターンを保持するのに十分な大きさを持っています。これはメモリアドレスを数値として表現するために使用されます。

uintptrの重要な特性は以下の通りです。

  • 数値表現: uintptrはポインタではなく、単なる数値です。そのため、それが指す値への参照は持ちません。
  • 型安全性なし: 通常のGoポインタとは異なり、uintptrはそれが指すものの型情報を持ちません。
  • ガベージコレクタによる追跡なし: Goのガベージコレクタは通常のポインタを追跡してメモリを自動的に管理しますが、uintptrは数値として扱われるため、ガベージコレクタによって追跡されません。これは非常に重要で、もしオブジェクトがuintptrによってのみ参照されている場合、ガベージコレクタはそのオブジェクトを移動または解放する可能性があり、uintptrが無効になることでクラッシュやセキュリティ上の問題を引き起こす可能性があります。
  • ポインタ演算: 通常のGoポインタではポインタ演算は許可されていませんが、uintptrを使用するとポインタ演算が可能です。これは通常、unsafe.Pointeruintptrに変換し、演算を行った後に再度unsafe.Pointerに戻すという形で行われます。

uintptrは、Cライブラリとの連携、unsafeパッケージを使用した低レベルなメモリ操作、システムコールなど、Goの型安全性をバイパスして直接メモリにアクセスする必要がある場合に主に使用されます。

Plan 9オペレーティングシステム

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソースをファイルとして表現し、それらをネットワーク越しに透過的にアクセスできるという特徴を持っています。Go言語の設計者の一部がPlan 9の開発に深く関わっていたため、Go言語の多くの側面(例えば、アセンブラの構文、ツールチェインの設計、一部のランタイムの挙動)にPlan 9の影響が見られます。

GoはPlan 9への実験的なポートを提供していますが、Plan 9は現代の主要なOS(Linux, Windows, macOS)とは異なるシステムコールやメモリ管理のメカニズムを持っています。そのため、GoランタイムはPlan 9固有の低レベルな実装(mem_plan9.cのようなファイル)を持つ必要があります。

技術的詳細

このコミットの技術的な核心は、C言語で書かれたGoランタイムのPlan 9固有のメモリ管理ファイルsrc/pkg/runtime/mem_plan9.cにおける関数シグネチャの不一致です。

Goランタイムの内部関数は、GoのツールチェインによってC言語の関数としてコンパイルされ、リンクされます。これらの関数は、Goのコードから呼び出される際に、Goの型システムとC言語の型システムの間で正しくマッピングされる必要があります。

問題のruntime·SysUsed関数は、GoランタイムがOSから取得したメモリの量をOSに通知する役割を担っています。この関数は、メモリブロックの開始アドレス(void *v)と、そのブロックのサイズ(バイト単位)を受け取ることを意図しています。サイズを表す引数は、メモリの大きさを表現するためにuintptr型であるべきです。

元のコードでは、runtime·SysUsed関数の定義がruntime·SysUsed(void *v, uintptr n)となっていました。ここで、引数名がnとなっています。しかし、Goランタイムの他の部分や、この関数が期待するセマンティクスからすると、この引数は「バイト数」を意味するnbytesという名前であるべきでした。

この引数名の不一致自体は、C言語のコンテキストでは通常、コンパイルエラーにはなりません。C言語では、関数定義と宣言で引数名が異なっていても問題ありません。しかし、Goのビルドシステムやリンカ、あるいはGoとCのインターフェースを扱う部分で、この引数名の不一致が問題を引き起こしていた可能性があります。特に、GoのツールチェインがCコードを処理する際に、特定の命名規則や期待されるシグネチャに厳密に従う必要がある場合、このような些細な不一致がビルドの失敗やランタイムエラーにつながることがあります。

この修正は、引数名をnからnbytesに変更することで、関数シグネチャをGoランタイムの他の部分や、この関数が意図するセマンティクスと一致させ、Plan 9ビルドの整合性を確保しました。

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

--- a/src/pkg/runtime/mem_plan9.c
+++ b/src/pkg/runtime/mem_plan9.c
@@ -56,7 +56,7 @@ runtime·SysUnused(void *v, uintptr nbytes)
 }
 
 void
-runtime·SysUsed(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr nbytes)
 {
 	USED(v, nbytes);
 }

コアとなるコードの解説

変更はsrc/pkg/runtime/mem_plan9.cファイル内のruntime·SysUsed関数の定義にあります。

  • 変更前:

    void
    runtime·SysUsed(void *v, uintptr n)
    {
    	USED(v, n);
    }
    

    ここでは、runtime·SysUsed関数の2番目の引数がuintptr nと定義されていました。関数本体ではUSED(v, n);と、このnが使用されています。

  • 変更後:

    void
    runtime·SysUsed(void *v, uintptr nbytes)
    {
    	USED(v, nbytes);
    }
    

    変更後では、2番目の引数名がuintptr nbytesに変更されています。関数本体もUSED(v, nbytes);と、新しい引数名に合わせて修正されています。

この変更は、引数の型(uintptr)はそのままに、引数名をnからnbytesへと修正したものです。この修正により、runtime·SysUsed関数のシグネチャが、その役割(バイト数を扱う)とGoランタイム全体の命名規則や期待されるインターフェースに合致するようになりました。これにより、Plan 9環境でのGoのビルドプロセスが正常に完了し、メモリ管理が意図通りに機能するようになります。

関連リンク

参考にした情報源リンク