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

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

このコミットは、Go言語のsyscallパッケージにおけるLinux固有のTgkillシステムコールの定義を修正するものです。具体的には、Tgkill関数のsig引数の型をintからSignal型に変更しています。これにより、シグナルに関する型安全性が向上し、よりGoらしいAPI設計に近づいています。

コミット

commit 33f7596f8d13a8114275b2ec3aa31791857f7554
Author: Russ Cox <rsc@golang.org>
Date:   Tue Feb 14 13:07:14 2012 -0500

    syscall: linux Tgkill takes a Signal too
    
    R=golang-dev, gri
    CC=golang-dev
    https://golang.org/cl/5649089

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

https://github.com/golang/go/commit/33f7596f8d13a8114275b2ec3aa31791857f7554

元コミット内容

syscall: linux Tgkill takes a Signal too

変更の背景

この変更の背景には、Go言語のsyscallパッケージにおけるシステムコール関数の引数型の一貫性と型安全性の向上が挙げられます。Linuxのtgkillシステムコールは、特定のプロセスグループ内の特定のスレッドにシグナルを送信するために使用されます。従来のGoのsyscallパッケージでは、このシグナルを表す引数が汎用的なint型として定義されていました。

しかし、Goのsyscallパッケージには、SIGTERMSIGKILLといった特定のシグナル定数を表すSignal型が既に存在していました。このSignal型を使用することで、シグナル関連の操作において、誤った整数値がシグナルとして渡されることを防ぎ、コードの可読性と保守性を向上させることができます。

このコミットは、Tgkill関数が他のシグナル関連関数と同様にSignal型を引数として受け取るようにすることで、APIの一貫性を保ち、開発者がより安全にシステムコールを扱えるようにすることを目的としています。これは、Go言語が提供する強力な型システムを活用し、低レベルのシステムプログラミングにおいても堅牢性を確保しようとする設計思想の一環と言えます。

前提知識の解説

システムコール (System Call)

システムコールは、オペレーティングシステム (OS) のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御、ネットワーク通信など、OSの機能にアクセスする際にシステムコールが使用されます。プログラムが直接ハードウェアにアクセスすることは通常許可されていないため、システムコールを介してOSに処理を依頼します。

tgkill システムコール

tgkillはLinuxカーネルが提供するシステムコールの一つで、特定のプロセスグループID (tgid) とスレッドID (tid) を持つスレッドに対して、指定されたシグナルを送信するために使用されます。これは、プロセス全体ではなく、特定の単一スレッドにシグナルを送りたい場合に特に有用です。例えば、デバッガが特定のスレッドを一時停止させたり、終了させたりする際に利用されることがあります。

シグナル (Signal)

シグナルは、Unix系OSにおいてプロセス間通信やイベント通知のために使用されるソフトウェア割り込みの一種です。例えば、SIGTERMはプロセスを終了させるためのシグナル、SIGKILLは強制的にプロセスを終了させるシグナル、SIGINTはCtrl+Cなどでプログラムを中断させるシグナルなどがあります。シグナルは、プロセスに対して非同期的にイベントを通知するメカニズムを提供します。

Go言語の syscall パッケージ

Go言語の標準ライブラリには、OSのシステムコールに直接アクセスするためのsyscallパッケージが含まれています。このパッケージは、低レベルのOS機能を利用する必要がある場合に用いられます。例えば、ファイルディスクリプタの操作、プロセス管理、ネットワークソケットの制御など、Goの標準ライブラリでは抽象化されているが、より詳細な制御が必要な場面で利用されます。

Signal 型 (Go言語 syscall パッケージ内)

Goのsyscallパッケージには、Signalという型が定義されています。これは、syscall.SIGTERMsyscall.SIGKILLといった、OSが定義するシグナル定数を表すための型です。この型を使用することで、シグナルを扱う際に整数値の誤用を防ぎ、コードの意図を明確にすることができます。例えば、func SendSignal(pid int, sig syscall.Signal) のように定義することで、sig引数にはシグナルとして有効な値のみが渡されることを期待できます。

zsyscall_linux_*.go ファイル群

Goのsyscallパッケージには、zsyscall_linux_386.gozsyscall_linux_amd64.gozsyscall_linux_arm.goといったファイル群が存在します。これらのファイルは、Goのツールチェーンによって自動生成されるもので、各アーキテクチャ(386、amd64、ARMなど)におけるシステムコールのラッパー関数や定数が定義されています。これらのファイルは通常、手動で編集されることはなく、mksyscall.plのようなスクリプトによって生成されます。

技術的詳細

このコミットは、Go言語のsyscallパッケージにおけるLinux固有のTgkillシステムコールのGo言語側の定義を変更しています。

具体的には、以下のファイルが変更されています。

  1. src/pkg/syscall/syscall_linux.go: このファイルは、Linuxシステムコールに関するGo言語のインターフェース定義が含まれています。ここでTgkill関数のシグナル引数の型がintからSignalに変更されています。

    • 変更前: //sysnb Tgkill(tgid int, tid int, sig int) (err error)
    • 変更後: //sysnb Tgkill(tgid int, tid int, sig Signal) (err error) この変更は、Goのsyscallパッケージがシステムコールをどのようにラップするかを定義するコメント行(//sysnb)に対するものです。このコメントは、mksyscall.plのようなツールが実際のシステムコールラッパーコードを生成する際の指示として機能します。
  2. src/pkg/syscall/zsyscall_linux_386.go

  3. src/pkg/syscall/zsyscall_linux_amd64.go

  4. src/pkg/syscall/zsyscall_linux_arm.go これらのファイルは、Goのビルドプロセス中に自動生成されるファイルであり、各CPUアーキテクチャ(x86、x86-64、ARM)向けの実際のシステムコール呼び出しラッパー関数が含まれています。syscall_linux.goの定義変更に伴い、これらの生成されたファイル内のTgkill関数のシグナル引数の型もintからSignalに自動的に更新されています。

    • 変更前: func Tgkill(tgid int, tid int, sig int) (err error) { ... }
    • 変更後: func Tgkill(tgid int, tid int, sig Signal) (err error) { ... } これらのファイルでは、RawSyscall関数を使用して実際のLinuxカーネルのtgkillシステムコールを呼び出しています。RawSyscallは、引数をuintptr型として受け取るため、GoのSignal型が最終的にuintptrにキャストされてシステムコールに渡されます。この変更は、Go言語レベルでの型チェックを強化し、開発者がTgkill関数を呼び出す際に、より適切な型(Signal型)を使用するように促すものです。

この変更により、Tgkill関数を呼び出す側は、int型の任意の整数ではなく、syscall.Signal型の値(例: syscall.SIGTERM)を渡すことが期待されるようになります。これにより、コンパイル時に型エラーを検出できるようになり、実行時エラーのリスクが低減されます。

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

このコミットにおけるコアとなるコードの変更は、src/pkg/syscall/syscall_linux.go ファイル内のTgkill関数の定義行です。

--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -856,7 +856,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
 //sys	Sync()
 //sysnb	Sysinfo(info *Sysinfo_t) (err error)
 //sys	Tee(rfd int, wfd int, len int, flags int) (n int64, err error)
-//sysnb	Tgkill(tgid int, tid int, sig int) (err error)
+//sysnb	Tgkill(tgid int, tid int, sig Signal) (err error)
 //sysnb	Times(tms *Tms) (ticks uintptr, err error)
 //sysnb	Umask(mask int) (oldmask int)
 //sysnb	Uname(buf *Utsname) (err error)

そして、この変更に追従して自動生成される各アーキテクチャ固有のファイル(例: src/pkg/syscall/zsyscall_linux_386.go)でも同様の変更が行われています。

--- a/src/pkg/syscall/zsyscall_linux_386.go
+++ b/src/pkg/syscall/zsyscall_linux_386.go
@@ -793,7 +793,7 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func Tgkill(tgid int, tid int, sig int) (err error) {
+func Tgkill(tgid int, tid int, sig Signal) (err error) {
 	_, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))\
 	if e1 != 0 {
 		err = e1

コアとなるコードの解説

この変更の核心は、TgkillシステムコールのGo言語ラッパーにおけるシグナル引数の型を、よりセマンティックで型安全なSignal型に統一することです。

  1. syscall_linux.go の変更:

    • //sysnb Tgkill(tgid int, tid int, sig int) (err error) から
    • //sysnb Tgkill(tgid int, tid int, sig Signal) (err error) へと変更されています。
    • この行は、Goのsyscallパッケージがシステムコールをどのようにラップするかを定義するメタデータのようなものです。//sysnbは、この関数がノンブロッキングシステムコールであり、Goのランタイムが直接呼び出すことを示唆しています。
    • sig int から sig Signal への変更は、Go言語のコンパイラに対して、Tgkill関数を呼び出す際にsig引数にはint型ではなくSignal型の値を渡す必要があることを指示します。これにより、開発者が誤って無効な整数値をシグナルとして渡すことを防ぎ、コンパイル時にエラーを検出できるようになります。
  2. zsyscall_linux_*.go ファイル群の変更:

    • これらのファイルは、syscall_linux.goの定義に基づいて自動生成されます。したがって、syscall_linux.goの変更が反映され、Tgkill関数のシグネチャが更新されます。
    • func Tgkill(tgid int, tid int, sig int) (err error) から
    • func Tgkill(tgid int, tid int, sig Signal) (err error) へと変更されています。
    • 関数本体の RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig)) の部分は変更されていません。これは、GoのSignal型が基底型として整数(int)を持つため、uintptr(sig)としてシステムコールに渡される際には、その整数値がそのまま利用されるためです。この変更は、Go言語レベルでの型チェックとAPIの明確化に焦点を当てています。

この変更により、Goのsyscallパッケージを使用する開発者は、Tgkill関数を呼び出す際に、syscall.SIGTERMのようなSignal型の定数を使用することが推奨され、より意図が明確で安全なコードを書くことができるようになります。これは、Go言語が提供する型システムを最大限に活用し、低レベルのシステムプログラミングにおいても堅牢性を確保しようとするGoの設計哲学を反映しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Linux tgkill システムコールに関するオンラインドキュメント
  • Go言語のsyscallパッケージのソースコード
  • Go言語のコミット履歴と関連するコードレビュー
  • Go言語のSignal型の定義に関する情報