[インデックス 18313] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにファイルロック機能を追加するものです。具体的には、Flock_t
構造体にLock
メソッドが追加され、fcntl
システムコールを介したファイルロック操作を可能にしています。また、32-bit Linuxシステムにおけるfcntl
システムコールの差異を吸収するためのファイルも新規追加されています。
変更されたファイルは以下の通りです。
src/pkg/syscall/consistency_unix_test.go
:Flock_t
構造体のフィールド型がint64
に変更されています。これは、Start
とLen
フィールドが32-bitシステムと64-bitシステムで一貫した型を持つようにするための修正です。src/pkg/syscall/flock.go
:Flock_t
構造体にLock
メソッドが追加され、fcntl
システムコールを呼び出すための共通ロジックが実装されています。このファイルは新規作成です。src/pkg/syscall/flock_linux_32bit.go
: 32-bit Linuxシステム(linux/386
およびlinux/arm
)において、fcntl
システムコールがSYS_FCNTL64
として扱われるべきであることを設定するinit
関数が含まれています。このファイルも新規作成です。
コミット
commit 5d3033c5907956fd982409dd4a543f6866dd675e
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Tue Jan 21 14:52:44 2014 -0800
syscall: add Flock_t.Lock method
Fixes #7059
R=golang-codereviews, iant, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/53470043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5d3033c5907956fd982409dd4a543f6866dd675e
元コミット内容
syscall: add Flock_t.Lock method
Fixes #7059
R=golang-codereviews, iant, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/53470043
変更の背景
このコミットは、Go言語のsyscall
パッケージにファイルロック機能を追加することを目的としています。コミットメッセージにあるFixes #7059
が示す通り、この変更はGoのIssue 7059「syscall: add flock(2)
」に対応するものです。
Issue 7059では、GoプログラムからUnix系のflock(2)
システムコール(またはfcntl(2)
のファイルロック機能)を直接利用できるようにする要望が出されていました。ファイルロックは、複数のプロセスが同じファイルに同時にアクセスする際に、データの整合性を保つために不可欠な機能です。例えば、ログファイルへの書き込み、設定ファイルの更新、データベースファイルの排他制御など、多くのアプリケーションで利用されます。
Goの標準ライブラリには、os
パッケージを通じて基本的なファイル操作は提供されていましたが、低レベルなファイルロック機能は直接サポートされていませんでした。そのため、Goでファイルロックを必要とするアプリケーションを開発する場合、Cgoを使ってC言語のライブラリを呼び出すか、あるいはGoのsyscall
パッケージを使って手動でシステムコールを構築する必要がありました。これは開発者にとって負担であり、移植性にも課題がありました。
このコミットは、syscall
パッケージにFlock_t
構造体とそれに関連するLock
メソッドを追加することで、Goから直接、よりGoらしい方法でファイルロックを操作できるようにし、開発者の利便性とコードの可読性・保守性を向上させることを目的としています。特に、fcntl
システムコールを用いたファイルロックは、flock(2)
よりも柔軟なロック(バイト範囲ロックなど)を提供できるため、より高度な制御が必要な場合に有用です。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
1. システムコール (Syscall)
システムコールは、オペレーティングシステム(OS)のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御、ネットワーク通信など、OSの機能にアクセスする際にはシステムコールが使われます。Go言語のsyscall
パッケージは、これらの低レベルなシステムコールをGoプログラムから直接呼び出すための機能を提供します。
2. ファイルロック (File Locking)
ファイルロックは、複数のプロセスが共有ファイルに同時にアクセスする際に、データの破損や競合状態を防ぐためのメカニズムです。ファイルロックには主に2つの種類があります。
- 共有ロック (Shared Lock / Read Lock): 複数のプロセスが同時にファイルを読み取ることができますが、書き込みはできません。
- 排他ロック (Exclusive Lock / Write Lock): 一度に1つのプロセスのみがファイルを読み書きできます。他のプロセスは、ロックが解除されるまで待機するか、エラーを受け取ります。
ファイルロックは、ファイル全体に適用されることもあれば、ファイルの一部(バイト範囲)に適用されることもあります。
3. fcntl(2)
システムコール
fcntl
(file control) はUnix系OSで利用されるシステムコールで、オープンされたファイルディスクリプタのプロパティを操作するために使用されます。ファイルロック機能もfcntl
を通じて提供されます。fcntl
を用いたファイルロックは、flock(2)
システムコールによるロックとは異なり、プロセス間で継承されず、より粒度の細かいバイト範囲ロックが可能です。
fcntl
システムコールでファイルロックを行う際には、以下の引数を使用します。
fd
: ロックを適用するファイルのファイルディスクリプタ。cmd
: 実行する操作(例:F_GETLK
でロック状態の取得、F_SETLK
で非ブロックロック設定、F_SETLKW
でブロックロック設定)。struct flock*
: ロックの種類、開始オフセット、長さ、プロセスIDなどの情報を含むflock
構造体へのポインタ。
4. flock
構造体 (Flock_t
in Go)
fcntl
システムコールでファイルロックを操作する際に使用されるデータ構造です。Goのsyscall
パッケージではFlock_t
として定義されています。この構造体には、ロックの種類(共有ロックか排他ロックか)、ロックの開始オフセット、ロックの長さ、ロックを保持しているプロセスのIDなどが含まれます。
一般的なflock
構造体のフィールドは以下の通りです(システムによって多少異なる場合があります)。
l_type
: ロックの種類 (F_RDLCK
(共有ロック),F_WRLCK
(排他ロック),F_UNLCK
(ロック解除))l_whence
:l_start
の基準位置 (SEEK_SET
(ファイルの先頭),SEEK_CUR
(現在の位置),SEEK_END
(ファイルの末尾))l_start
: ロックの開始オフセットl_len
: ロックの長さ (0はファイルの最後まで)l_pid
: ロックを保持しているプロセスのID (主にF_GETLK
で使用)
5. 32-bit と 64-bit システムにおけるシステムコールの違い
Unix系OSでは、システムコールの番号や引数の型、構造体のレイアウトが、CPUアーキテクチャ(32-bitか64-bitか)によって異なる場合があります。特に、fcntl
のような汎用的なシステムコールは、32-bitシステムではファイルオフセットや長さが32-bit整数で表現されるため、大きなファイル(2GB以上)を扱う際に問題が生じることがありました。
この問題を解決するため、32-bit Linuxでは、大きなファイルオフセットを扱えるようにfcntl64
というシステムコールが導入されました。これは、fcntl
と同じ機能を提供しますが、引数として64-bitのオフセットと長さを取ります。Goのsyscall
パッケージは、このようなアーキテクチャ間の差異を吸収し、開発者が意識することなく正しいシステムコールが呼び出されるように設計されています。このコミットでは、まさにこの32-bit Linuxにおけるfcntl
の差異を吸収するためのロジックが追加されています。
技術的詳細
このコミットの技術的な核心は、Goのsyscall
パッケージが、異なるUnix系OSやアーキテクチャ(特に32-bit Linux)間でfcntl
システムコールの挙動の違いをどのように吸収し、一貫したファイルロックAPIを提供するかにあります。
Flock_t
構造体の修正
src/pkg/syscall/consistency_unix_test.go
の変更は、Flock_t
構造体のStart
とLen
フィールドの型をint64
に明示的に変更しています。これは、異なるアーキテクチャ(32-bitと64-bit)間でこれらのフィールドのサイズとアライメントを統一し、fcntl
システムコールに渡す際の互換性を確保するためです。以前はリテラル0
が使われていましたが、これはコンパイラによってデフォルトのint
型(32-bitシステムでは32-bit、64-bitシステムでは64-bit)に推論される可能性があり、クロスコンパイルや異なる環境での実行時に問題を引き起こす可能性がありました。int64(0)
と明示することで、常に64-bit整数として扱われることが保証されます。
flock.go
の新規追加とFlock_t.Lock
メソッド
src/pkg/syscall/flock.go
は、Flock_t
構造体にLock
メソッドを追加し、ファイルロック操作の主要なロジックをカプセル化しています。
// +build linux darwin freebsd openbsd netbsd dragonfly
package syscall
import "unsafe"
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
var fcntl64Syscall uintptr = SYS_FCNTL
// Lock performs a fcntl syscall for F_GETLK, F_SETLK or F_SETLKW commands.
func (lk *Flock_t) Lock(fd uintptr, cmd int) error {
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
if errno == 0 {
return nil
}
return errno
}
- ビルドタグ:
// +build linux darwin freebsd openbsd netbsd dragonfly
は、このファイルが指定されたOSでのみコンパイルされることを示します。これは、fcntl
ベースのファイルロックがこれらのUnix系OSで共通して利用可能であるためです。 fcntl64Syscall
変数: このグローバル変数は、fcntl
システムコールの番号を保持します。デフォルトではSYS_FCNTL
に初期化されます。しかし、後述するflock_linux_32bit.go
によって、32-bit LinuxシステムではSYS_FCNTL64
に上書きされます。これにより、Goのコードは常に正しいfcntl
システムコールを呼び出すことができます。Lock
メソッド:fd uintptr
: ロックを適用するファイルのファイルディスクリプタ。cmd int
:F_GETLK
,F_SETLK
,F_SETLKW
などのfcntl
コマンド。Syscall
関数: Goのsyscall
パッケージが提供する低レベルなシステムコール呼び出し関数です。- 第一引数にはシステムコール番号(
fcntl64Syscall
)を渡します。 - 第二引数にはファイルディスクリプタ
fd
を渡します。 - 第三引数にはコマンド
cmd
を渡します。 - 第四引数には
Flock_t
構造体lk
へのポインタをuintptr
にキャストして渡します。unsafe.Pointer
を使用しているのは、Goの型安全性を一時的にバイパスして、構造体のアドレスをシステムコールに渡すためです。
- 第一引数にはシステムコール番号(
- 戻り値: システムコールの結果として返されるエラーコード(
errno
)をチェックし、エラーがあればそれを返します。errno == 0
は成功を意味します。
flock_linux_32bit.go
の新規追加とinit
関数
src/pkg/syscall/flock_linux_32bit.go
は、32-bit Linuxシステムに特化したロジックを提供します。
// +build linux,386 linux,arm
package syscall
func init() {
// On 32-bit Linux systems, the fcntl syscall that matches Go's
// Flock_t type is SYS_FCNTL64, not SYS_FCNTL.
fcntl64Syscall = SYS_FCNTL64
}
- ビルドタグ:
// +build linux,386 linux,arm
は、このファイルがLinux上で32-bitアーキテクチャ(Intel 386互換およびARM)の場合にのみコンパイルされることを示します。 init
関数: Goのinit
関数は、パッケージが初期化される際に自動的に実行されます。このinit
関数では、グローバル変数fcntl64Syscall
をSYS_FCNTL64
に上書きしています。これにより、32-bit Linuxシステムでは、Flock_t.Lock
メソッドが内部的にSYS_FCNTL64
システムコールを呼び出すようになります。これは、32-bitシステムで64-bitオフセットを扱うためのfcntl
のバリアントであり、GoのFlock_t
構造体(Start
とLen
がint64
)と整合性が取れます。
この設計により、Goのsyscall
パッケージは、異なるOSやアーキテクチャの差異を内部で吸収し、開発者には一貫したFlock_t.Lock
インターフェースを提供するという、移植性と利便性を両立させています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の3つのファイルにまたがっています。
-
src/pkg/syscall/consistency_unix_test.go
Flock_t
構造体の初期化部分で、Start
とLen
フィールドにint64(0)
が明示的に指定されました。--- a/src/pkg/syscall/consistency_unix_test.go +++ b/src/pkg/syscall/consistency_unix_test.go @@ -37,8 +37,8 @@ func _() { _ = syscall.Flock_t{ \tType: int16(0), \tWhence: int16(0), -\t\tStart: 0, -\t\tLen: 0, +\t\tStart: int64(0), +\t\tLen: int64(0), \tPid: int32(0), } }
-
src/pkg/syscall/flock.go
(新規ファイル)Flock_t
構造体にLock
メソッドが追加されました。fcntl64Syscall
というuintptr
型のグローバル変数が定義され、デフォルトでSYS_FCNTL
に初期化されます。// +build linux darwin freebsd openbsd netbsd dragonfly // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syscall import "unsafe" var fcntl64Syscall uintptr = SYS_FCNTL func (lk *Flock_t) Lock(fd uintptr, cmd int) error { _, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk))) if errno == 0 { return nil } return errno }
-
src/pkg/syscall/flock_linux_32bit.go
(新規ファイル)- 32-bit Linuxシステム(
linux,386
およびlinux,arm
)でのみコンパイルされるinit
関数が追加されました。 - この
init
関数内で、fcntl64Syscall
変数がSYS_FCNTL64
に上書きされます。// +build linux,386 linux,arm // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syscall func init() { // On 32-bit Linux systems, the fcntl syscall that matches Go's // Flock_t type is SYS_FCNTL64, not SYS_FCNTL. fcntl64Syscall = SYS_FCNTL64 }
- 32-bit Linuxシステム(
コアとなるコードの解説
src/pkg/syscall/consistency_unix_test.go
の変更
このテストファイルの変更は、Flock_t
構造体のStart
とLen
フィールドが、32-bitと64-bitの両方のシステムで一貫してint64
として扱われることを保証するためのものです。以前はリテラル0
が使われていましたが、これはGoの型推論によってプラットフォーム依存のint
型(32-bitシステムでは32-bit、64-bitシステムでは64-bit)に解決される可能性がありました。int64(0)
と明示することで、これらのフィールドが常に64-bit整数として扱われるようになり、fcntl
システムコールに渡されるFlock_t
構造体のメモリレイアウトが、異なるアーキテクチャ間で安定します。これは、特に32-bitシステムでfcntl64
システムコールを使用する際に、正しい引数構造を渡すために重要です。
src/pkg/syscall/flock.go
の新規追加
このファイルは、Goプログラムからfcntl
システムコールを用いたファイルロックを操作するための主要なAPIを提供します。
-
fcntl64Syscall
変数:- この変数は、実際に呼び出す
fcntl
システムコールの番号を動的に決定するためのキーポイントです。 - デフォルトでは
SYS_FCNTL
に設定されています。これは、ほとんどの64-bit Unix系システムで標準的なfcntl
システムコールを指します。 - しかし、32-bit Linuxシステムでは、
fcntl
が64-bitオフセットを扱えないため、fcntl64
という別のシステムコール(SYS_FCNTL64
)を使用する必要があります。この変数を介することで、Goは実行環境に応じて適切なシステムコールを透過的に選択できます。
- この変数は、実際に呼び出す
-
Flock_t.Lock
メソッド:- このメソッドは、
Flock_t
構造体のインスタンスに対して呼び出され、指定されたファイルディスクリプタfd
とコマンドcmd
(例:F_SETLK
,F_SETLKW
)を使ってファイルロック操作を実行します。 Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
の行が核心です。fcntl64Syscall
: 前述の通り、環境に応じてSYS_FCNTL
またはSYS_FCNTL64
のいずれかのシステムコール番号が使用されます。fd
: ロック対象のファイルディスクリプタ。uintptr(cmd)
: 実行するfcntl
コマンド。uintptr(unsafe.Pointer(lk))
:Flock_t
構造体lk
のアドレスをシステムコールに渡すために、unsafe.Pointer
を使って型安全性を一時的に解除し、uintptr
に変換しています。これにより、カーネルはメモリ上のFlock_t
構造体の内容を読み取り、ロック操作を実行できます。
- システムコールが成功した場合(
errno == 0
)、nil
エラーを返します。失敗した場合は、対応するerrno
を返します。
- このメソッドは、
src/pkg/syscall/flock_linux_32bit.go
の新規追加
このファイルは、32-bit Linuxシステムに特化した初期化ロジックを提供します。
-
ビルドタグ
// +build linux,386 linux,arm
:- このタグにより、このファイルはLinux上で32-bitアーキテクチャ(
386
またはarm
)の場合にのみコンパイルされます。これにより、64-bit Linuxや他のOSではこのコードは含まれず、無関係な変更を避けることができます。
- このタグにより、このファイルはLinux上で32-bitアーキテクチャ(
-
init
関数:- Goの
init
関数は、パッケージがロードされる際に自動的に実行されます。 - この
init
関数内で、fcntl64Syscall
グローバル変数がSYS_FCNTL64
に上書きされます。 - この上書きは、32-bit Linuxシステムにおいて、
fcntl
システムコールが64-bitオフセットを扱うためのfcntl64
システムコール(SYS_FCNTL64
)として提供されているというOSの特性に対応するためのものです。GoのFlock_t
構造体のStart
とLen
フィールドがint64
であるため、このSYS_FCNTL64
を呼び出すことで、Goの構造体とOSのシステムコールが正しく連携し、大きなファイルに対するロックも適切に機能するようになります。
- Goの
これらの変更により、Goのsyscall
パッケージは、異なるUnix系OSやアーキテクチャの差異を内部で吸収し、開発者には一貫したFlock_t.Lock
インターフェースを提供することで、ファイルロック機能の移植性と利便性を大幅に向上させています。
関連リンク
- Go Issue 7059: syscall: add flock(2)
- Go CL 53470043: https://golang.org/cl/53470043
参考にした情報源リンク
fcntl(2)
man page: https://man7.org/linux/man-pages/man2/fcntl.2.htmlflock(2)
man page: https://man7.org/linux/man-pages/man2/flock.2.html- Go
syscall
package documentation: https://pkg.go.dev/syscall - Go
unsafe
package documentation: https://pkg.go.dev/unsafe - Go
init
functions: https://go.dev/doc/effective_go#init - Go build constraints: https://go.dev/pkg/go/build/#hdr-Build_Constraints
- 32-bit vs 64-bit Linux syscalls (general concept): https://www.kernel.org/doc/html/latest/arch/x86/entry_64.html (This is a general reference for x86-64, but the concept of syscall differences applies.)
SYS_FCNTL
andSYS_FCNTL64
in Linux: https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/unistd_32.h (Example for 32-bit x86)Flock_t
definition in Go source: https://github.com/golang/go/blob/master/src/syscall/types_unix.go (Actual path may vary by Go version, but this is a common location forFlock_t
definition)- Go
Syscall
function: https://github.com/golang/go/blob/master/src/syscall/syscall_unix.go (Actual path may vary by Go version, but this is a common location forSyscall
definition)