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

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

このコミットは、Go言語のsyscallパッケージにLinux向けのSetpriorityおよびGetpriorityシステムコールを追加し、それらの一貫性テストを導入するものです。これにより、Goプログラムからプロセスのスケジューリング優先度を操作できるようになります。

コミット

commit 46e30c7d70fd30b5d67b93625cf97110dc751f68
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Jun 11 02:47:04 2013 +0800

    syscall: add {Set,Get}priority for Linux, and consistency tests
    
    R=golang-dev, iant, bradfitz, dave
    CC=golang-dev
    https://golang.org/cl/7430044

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

https://github.com/golang/go/commit/46e30c7d70fd30b5d67b93625cf97110dc751f68

元コミット内容

syscall: add {Set,Get}priority for Linux, and consistency tests

R=golang-dev, iant, bradfitz, dave
CC=golang-dev
https://golang.org/cl/7430044

変更の背景

Go言語のsyscallパッケージは、オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。これにより、GoプログラムはOSの機能を直接利用できます。このコミットが作成された背景には、Linux環境においてプロセスのスケジューリング優先度をプログラムから動的に制御する機能が不足していたことが挙げられます。

SetpriorityおよびGetpriorityシステムコールは、Unix系OSにおいてプロセス、プロセスグループ、またはユーザーのスケジューリング優先度(nice値)を設定・取得するために使用されます。これらのシステムコールがGoのsyscallパッケージに統合されることで、Goで記述されたアプリケーションが、例えば以下のような高度なシステム管理タスクを実行できるようになります。

  • リソース管理: 重要なバックグラウンドプロセスやリアルタイム処理を行うアプリケーションの優先度を上げ、システムリソースをより多く割り当てる。
  • 負荷分散: 優先度の低いタスクのnice値を上げて、CPU使用率を抑える。
  • システム監視: 実行中のプロセスやユーザーの現在の優先度を監視し、システムのパフォーマンスボトルネックを特定する。

この機能の追加は、Goがより広範なシステムプログラミングのユースケースに対応するための重要なステップでした。特に、Linux環境でのよりきめ細やかなプロセス制御を可能にすることで、Goアプリケーションの適用範囲が広がります。

前提知識の解説

システムコール (System Call)

システムコールは、オペレーティングシステム (OS) のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイルI/O、メモリ管理、プロセス管理、ネットワーク通信など、OSの根幹をなす機能はシステムコールを通じて提供されます。プログラムがこれらの機能を利用する際には、直接ハードウェアにアクセスするのではなく、システムコールを介してカーネルに処理を依頼します。これにより、システムの安定性とセキュリティが保たれます。

Go言語のsyscallパッケージは、GoプログラムからこれらのOSネイティブなシステムコールを呼び出すための薄いラッパーを提供します。これにより、Goは低レベルなシステムプログラミングも可能にしています。

プロセスのスケジューリング優先度 (nice値)

Unix系OSでは、プロセスの実行優先度を制御するために「nice値」という概念が用いられます。nice値は、プロセスのスケジューリング優先度に対するヒントとしてカーネルに与えられます。

  • nice値の範囲: 通常、-20から+19までの整数値を取ります。
  • 値の意味:
    • 低いnice値 (例: -20): 優先度が高いことを意味し、より多くのCPU時間を割り当てられやすくなります。ただし、nice値を下げる(優先度を上げる)操作は、通常root権限が必要です。
    • 高いnice値 (例: +19): 優先度が低いことを意味し、他のプロセスにCPU時間を譲りやすくなります。これは、バックグラウンドで実行される非緊急なタスクに適しています。
  • setpriority / getpriority システムコール:
    • setpriority(which, who, prio): 指定されたwhich(プロセス、プロセスグループ、ユーザー)とwho(ID)に対して、prio(nice値)を設定します。
    • getpriority(which, who): 指定されたwhichwhoの現在のnice値を取得します。
  • which 引数:
    • PRIO_PROCESS: 個別のプロセスに対して優先度を設定/取得します。whoはプロセスID (PID) です。
    • PRIO_PGRP: プロセスグループに対して優先度を設定/取得します。whoはプロセスグループID (PGID) です。
    • PRIO_USER: 特定のユーザーが所有するすべてのプロセスに対して優先度を設定/取得します。whoはユーザーID (UID) です。

これらのシステムコールは、システム管理者や特定のアプリケーションが、システムリソースの割り当てを最適化するために利用されます。

zerrors_*.go および zsyscall_*.go ファイル

Goのsyscallパッケージでは、OS固有の定数やシステムコール関数が自動生成されています。

  • zerrors_*.go: これらのファイルは、OS固有のエラーコードや定数(例: PRIO_PROCESS, PRIO_PGRP, PRIO_USERなど)を定義しています。これらは通常、C言語のヘッダーファイルからスクリプトによって自動生成されます。
  • zsyscall_*.go: これらのファイルは、OS固有のシステムコール関数を定義しています。Goの関数シグネチャから、対応するCのシステムコールを呼び出すためのラッパーコードが自動生成されます。

これらの自動生成ファイルは、Goが様々なOSアーキテクチャで動作するために不可欠であり、手動でのメンテナンスの手間を省き、一貫性を保つ役割を担っています。

技術的詳細

このコミットの技術的な核心は、Linuxカーネルが提供するsetpriority(2)およびgetpriority(2)システムコールをGoのsyscallパッケージに統合することです。

  1. システムコール関数の追加:

    • src/pkg/syscall/syscall_linux.go: このファイルに、GetprioritySetpriorityのGo言語側の関数シグネチャが追加されます。これらは//sysディレクティブによって、後述のzsyscall_linux_*.goファイルに実際のシステムコール呼び出しコードが自動生成されることを示します。
    • src/pkg/syscall/zsyscall_linux_386.go, src/pkg/syscall/zsyscall_linux_amd64.go, src/pkg/syscall/zsyscall_linux_arm.go: これらのファイルは、mksyscall.shスクリプトによって自動生成されます。このコミットでは、GetprioritySetpriorityに対応する実際のシステムコール呼び出し(SyscallまたはRawSyscallを使用)が、各アーキテクチャ(386, amd64, arm)向けに追加されています。例えば、GetprioritySYS_GETPRIORITYシステムコール番号を使用してカーネルを呼び出し、戻り値から優先度とエラーを取得します。
  2. 定数の追加:

    • src/pkg/syscall/mkerrors.sh: このスクリプトは、OS固有の定数を生成するために使用されます。このコミットでは、PRIO_PROCESS, PRIO_PGRP, PRIO_USERといった優先度設定の対象を示す定数が、このスクリプトによって生成されるように変更が加えられています。具体的には、#include <sys/time.h>が追加され、PRIO_で始まる定数が含まれるように正規表現が更新されています。
    • src/pkg/syscall/zerrors_*.go: mkerrors.shスクリプトの実行により、zerrors_darwin_386.go, zerrors_linux_amd64.goなど、各OSおよびアーキテクチャ固有のzerrors_*.goファイルにPRIO_PGRP, PRIO_PROCESS, PRIO_USERの定数定義が追加されます。これらの定数は、SetpriorityGetpriority関数を呼び出す際に、どのエンティティ(プロセス、プロセスグループ、ユーザー)の優先度を操作するかを指定するために使用されます。
  3. 一貫性テストの追加:

    • src/pkg/syscall/consistency_unix_test.go: 新しいテストファイルが追加されています。このテストは、SetpriorityGetpriority関数、および関連する定数(PRIO_USER, PRIO_PROCESS, PRIO_PGRP)が、FreeBSD, Darwin (macOS), Linux, NetBSD, OpenBSDといった主要なUnix系OSで一貫して利用可能であることを確認します。これは、Goのクロスプラットフォーム互換性を維持するための重要なテストです。このテストは、実際にシステムコールを実行するのではなく、コンパイル時にこれらの関数と定数が存在し、正しい型シグネチャを持っていることを検証するものです。

これらの変更により、GoプログラムはLinux上でプロセスの優先度を安全かつ効率的に操作できるようになります。また、他のUnix系OSとの互換性も考慮されており、Goのsyscallパッケージの堅牢性が向上しています。

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

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  1. src/pkg/syscall/consistency_unix_test.go:

    • 新規追加されたテストファイル。
    • SetpriorityGetpriority関数、およびPRIO_USER, PRIO_PROCESS, PRIO_PGRP定数が、Unix系OS(FreeBSD, Darwin, Linux, NetBSD, OpenBSD)で利用可能であることをコンパイル時に検証する。
    // +build freebsd darwin linux netbsd openbsd
    
    package syscall_test
    
    import "syscall"
    
    // {Set,Get}priority and needed constants for them
    func _() {
    	var (
    		_ func(int, int, int) error   = syscall.Setpriority
    		_ func(int, int) (int, error) = syscall.Getpriority
    	)
    	const (
    		_ int = syscall.PRIO_USER
    		_ int = syscall.PRIO_PROCESS
    		_ int = syscall.PRIO_PGRP
    	)
    }
    
  2. src/pkg/syscall/mkerrors.sh:

    • sys/time.hのインクルードを追加。
    • PRIO_で始まる定数(PRIO_PROCESS, PRIO_PGRP, PRIO_USER)がエラー定数として認識され、zerrors_*.goファイルに生成されるように正規表現を更新。
    --- a/src/pkg/syscall/mkerrors.sh
    +++ b/src/pkg/syscall/mkerrors.sh
    @@ -71,6 +71,7 @@ includes_Linux='\
     #include <sys/prctl.h>\
     #include <sys/stat.h>\
     #include <sys/types.h>\
    +#include <sys/time.h>\
     #include <sys/socket.h>\
     #include <linux/if_addr.h>\
     #include <linux/if_ether.h>\
    @@ -222,6 +223,7 @@ ccflags="$@"\
     		$2 ~ /^BIOC/ ||\
     		$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||\
     		$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||\
    +\t\t$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||\
     		$2 !~ /^(BPF_TIMEVAL)$/ &&\
     		$2 ~ /^(BPF|DLT)_/ ||\
     		$2 !~ "WMESGLEN" &&
    
  3. src/pkg/syscall/syscall_linux.go:

    • GetprioritySetpriorityのGo言語側の関数シグネチャを追加。//sysディレクティブにより、対応するシステムコールが自動生成されることを示す。
    • 既存のコメントからGetprioritySetpriorityのエントリを削除(重複を避けるため)。
    --- a/src/pkg/syscall/syscall_linux.go
    +++ b/src/pkg/syscall/syscall_linux.go
    @@ -926,6 +926,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
     //sysnb	Getpgrp() (pid int)\
     //sysnb	Getpid() (pid int)\
     //sysnb	Getppid() (ppid int)\
    +//sys	Getpriority(which int, who int) (prio int, err error)\
     //sysnb	Getrusage(who int, rusage *Rusage) (err error)\
     //sysnb	Gettid() (tid int)\
     //sys	Getxattr(path string, attr string, dest []byte) (sz int, err error)\
    @@ -957,6 +958,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
     //sysnb	Setsid() (pid int, err error)\
     //sysnb	Settimeofday(tv *Timeval) (err error)\
     //sysnb	Setuid(uid int) (err error)\
    +//sys	Setpriority(which int, who int, prio int) (err error)\
     //sys	Setxattr(path string, attr string, data []byte, flags int) (err error)\
     //sys	Symlink(oldpath string, newpath string) (err error)\
     //sys	Sync()\
    @@ -1036,7 +1038,6 @@ func Munmap(b []byte) (err error) {\
     // GetThreadArea\
     // Getitimer\
     // Getpmsg\
    -// Getpriority\
     // IoCancel\
     // IoDestroy\
     // IoGetevents\
    @@ -1112,7 +1113,6 @@ func Munmap(b []byte) (err error) {\
     // SetRobustList\
     // SetThreadArea\
     // SetTidAddress\
    -// Setpriority\
     // Shmat\
     // Shmctl\
     // Shmdt\
    
  4. src/pkg/syscall/zerrors_*.go (例: zerrors_linux_amd64.go):

    • mkerrors.shの実行により、PRIO_PGRP, PRIO_PROCESS, PRIO_USERの定数定義が追加。
    --- a/src/pkg/syscall/zerrors_linux_amd64.go
    +++ b/src/pkg/syscall/zerrors_linux_amd64.go
    @@ -658,6 +658,9 @@ const (\
      	PACKET_RECV_OUTPUT               = 0x3\
      	PACKET_RX_RING                   = 0x5\
      	PACKET_STATISTICS                = 0x6\
    +\tPRIO_PGRP                        = 0x1\
    +\tPRIO_PROCESS                     = 0x0\
    +\tPRIO_USER                        = 0x2\
      	PROT_EXEC                        = 0x4\
      	PROT_GROWSDOWN                   = 0x1000000\
      	PROT_GROWSUP                     = 0x2000000\
    
  5. src/pkg/syscall/zsyscall_linux_*.go (例: zsyscall_linux_amd64.go):

    • GetprioritySetpriorityの実際のシステムコール呼び出しラッパー関数が追加。
    --- a/src/pkg/syscall/zsyscall_linux_amd64.go
    +++ b/src/pkg/syscall/zsyscall_linux_amd64.go
    @@ -534,6 +534,17 @@ func Getppid() (ppid int) {\
      \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\
      \n\
    +func Getpriority(which int, who int) (prio int, err error) {\
    +\tr0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)\
    +\tprio = int(r0)\
    +\tif e1 != 0 {\
    +\t\terr = e1\
    +\t}\
    +\treturn\
    +}\
    +\n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\
      \n func Getrusage(who int, rusage *Rusage) (err error) {\
      	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)\
      \tif e1 != 0 {\
    @@ -994,6 +1005,16 @@ func Setuid(uid int) (err error) {\
      \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\
      \n\
    +func Setpriority(which int, who int, prio int) (err error) {\
    +\t_, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))\
    +\tif e1 != 0 {\
    +\t\terr = e1\
    +\t}\
    +\treturn\
    +}\
    +\n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\
      \n func Setxattr(path string, attr string, data []byte, flags int) (err error) {\
      	var _p0 *byte\
      	_p0, err = BytePtrFromString(path)\
    

コアとなるコードの解説

src/pkg/syscall/consistency_unix_test.go

このファイルは、Goのクロスプラットフォーム互換性を保証するための重要な役割を担っています。// +build freebsd darwin linux netbsd openbsdというビルドタグは、このファイルが指定されたUnix系OSでのみコンパイルされることを意味します。

テスト関数_()は、実際にSetpriorityGetpriorityを呼び出すわけではありません。その代わりに、これらの関数と関連する定数(PRIO_USER, PRIO_PROCESS, PRIO_PGRP)が、Goのsyscallパッケージ内で期待される型シグネチャと値で存在するかどうかをコンパイル時に検証します。

  • _ func(int, int, int) error = syscall.Setpriority: syscall.Setpriorityfunc(int, int, int) errorというシグネチャを持つ関数であることを確認します。
  • _ func(int, int) (int, error) = syscall.Getpriority: syscall.Getpriorityfunc(int, int) (int, error)というシグネチャを持つ関数であることを確認します。
  • _ int = syscall.PRIO_USER: syscall.PRIO_USERint型であることを確認します。他のPRIO_PROCESSPRIO_PGRPも同様です。

この「コンパイル時テスト」は、異なるOSやアーキテクチャでGoのsyscallパッケージが正しく生成され、必要なインターフェースを提供していることを保証する、効率的かつ効果的な方法です。

src/pkg/syscall/mkerrors.sh

このシェルスクリプトは、Goのsyscallパッケージで使用されるOS固有の定数を自動生成するためのものです。C言語のヘッダーファイルを解析し、Goのソースコードとして出力します。

変更点である#include <sys/time.h>の追加は、getprioritysetpriorityに関連する定数(PRIO_PROCESSなど)が定義されているヘッダーファイルが、Linux環境で正しく読み込まれるようにするために必要です。

また、$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||という行の追加は、スクリプトがCヘッダーファイルからPRIO_PROCESSPRIO_PGRPPRIO_USERといった定数を抽出し、Goのzerrors_*.goファイルに含めるように指示しています。これにより、Goプログラムからこれらの定数を直接参照できるようになります。

src/pkg/syscall/syscall_linux.go

このファイルは、Linux固有のシステムコールに対するGo言語のインターフェースを定義しています。//sysディレクティブは、Goのツールチェーンがこの行を解析し、対応するシステムコールラッパー関数をzsyscall_linux_*.goファイルに自動生成するためのマーカーです。

  • //sys Getpriority(which int, who int) (prio int, err error): Getpriority関数が、whichwhoという2つのint型引数を取り、prioというint型とerrというerror型を返すことを宣言しています。
  • //sys Setpriority(which int, who int, prio int) (err error): Setpriority関数が、which, who, prioという3つのint型引数を取り、errというerror型を返すことを宣言しています。

これらの宣言により、Go開発者はGoの通常の関数呼び出し構文でシステムコールを利用できるようになり、低レベルなシステムコール番号やレジスタ操作を意識する必要がなくなります。

src/pkg/syscall/zsyscall_linux_*.go (例: zsyscall_linux_amd64.go)

これらのファイルは、syscall_linux.go//sysディレクティブに基づいて自動生成される、実際のシステムコール呼び出しを行うGoコードを含んでいます。

Getpriority関数の実装例(amd64アーキテクチャの場合):

func Getpriority(which int, who int) (prio int, err error) {
	r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
	prio = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}
  • Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0): これは、Linuxカーネルのgetpriorityシステムコールを呼び出す部分です。
    • SYS_GETPRIORITY: getpriorityシステムコールに対応するシステムコール番号です。これはOSとアーキテクチャによって異なります。
    • uintptr(which), uintptr(who): システムコールに渡される引数は、通常uintptr型にキャストされます。
    • r0, _, e1: システムコールからの戻り値を受け取ります。r0はプライマリな戻り値(この場合は優先度)、e1はエラーコードです。
  • prio = int(r0): システムコールからの戻り値r0int型に変換してprio変数に格納します。
  • if e1 != 0 { err = e1 }: エラーコードe1が0でない場合、エラーが発生したことを示し、err変数に設定します。

Setpriority関数も同様に、SYS_SETPRIORITYシステムコールを呼び出し、引数としてwhich, who, prioを渡します。

これらの自動生成されたラッパー関数は、GoプログラムがOSの低レベルな機能にアクセスするための安全で型付けされたインターフェースを提供し、Goのポータビリティとシステムプログラミング能力を向上させています。

関連リンク

参考にした情報源リンク

  • Go syscall package source code (relevant files):
    • src/pkg/syscall/consistency_unix_test.go
    • src/pkg/syscall/mkerrors.sh
    • src/pkg/syscall/syscall_linux.go
    • src/pkg/syscall/zerrors_linux_amd64.go (and other zerrors_*.go files)
    • src/pkg/syscall/zsyscall_linux_amd64.go (and other zsyscall_*.go files)
  • Linux man pages for getpriority and setpriority.
  • Go CL 7430044: https://golang.org/cl/7430044
  • Go issue tracker (related issues, if any, would be linked from the CL or commit message).
  • General knowledge about Unix system programming and Go's syscall package.