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

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

このコミットは、Go言語のnetパッケージにおけるファイルディスクリプタ (FD) の複製処理を最適化し、より効率的かつ堅牢にするための変更です。具体的には、F_DUPFD_CLOEXECというfcntlシステムコールフラグを使用することで、FDの複製とCLOEXECフラグの設定を単一のシステムコールで実行するように改善しています。これにより、システムコール回数の削減と、syscall.ForkLockの取得回避を実現しています。

コミット

commit 37feacf623dc95a3c6332640689f53a5baa85dbc
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Aug 5 15:43:45 2013 -0700

    net: use F_DUPFD_CLOEXEC when duping fds
    
    This means that in the common case (modern kernel), we only
    make 1 system call to dup instead of two, and we also avoid
    grabbing the syscall.ForkLock.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/12476043

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

https://github.com/golang/go/commit/37feacf623dc95a3c6332640689f53a5baa85dbc

元コミット内容

net: use F_DUPFD_CLOEXEC when duping fds

このコミットは、ファイルディスクリプタを複製する際にF_DUPFD_CLOEXECを使用するようにnetパッケージを変更します。

これにより、一般的なケース(モダンなカーネル)では、2回のdupシステムコールではなく1回のシステムコールで済み、syscall.ForkLockの取得も回避できます。

変更の背景

Go言語のnetパッケージは、ネットワーク接続を扱う上で、ソケットなどのファイルディスクリプタを頻繁に複製(dup)します。従来のFD複製とCLOEXECフラグ設定のプロセスは、以下の2つのシステムコールを必要としていました。

  1. dup(): 既存のファイルディスクリプタを複製し、新しいFDを返す。
  2. fcntl(F_SETFD, FD_CLOEXEC): 新しいFDに対してCLOEXEC (Close-on-exec) フラグを設定する。

この2段階のプロセスにはいくつかの問題がありました。

  • システムコールオーバーヘッド: 2つのシステムコールは、それぞれカーネルモードへの遷移を伴い、パフォーマンスのオーバーヘッドが発生します。
  • 競合状態の可能性: dup()fcntl()の間に、新しいFDが子プロセスに継承されてしまう可能性がありました。特に、fork()exec()が連続して行われるような状況では、意図しないFDのリークが発生するリスクがありました。
  • syscall.ForkLockの利用: Goのランタイムでは、fork/execに関連する操作の際にデッドロックを防ぐためにsyscall.ForkLockというグローバルロックを使用しています。従来の2段階のFD複製プロセスでは、syscall.ForkLockを取得する必要があり、これが並行処理のボトルネックとなる可能性がありました。

これらの問題を解決するため、Linuxカーネル(および他のUnix系OS)ではF_DUPFD_CLOEXECというfcntlフラグが導入されました。このフラグを使用すると、FDの複製とCLOEXECフラグの設定を単一の原子的な操作として実行できます。このコミットは、このモダンなカーネル機能を利用して、netパッケージのFD複製処理を最適化することを目的としています。

前提知識の解説

ファイルディスクリプタ (File Descriptor, FD)

Unix系オペレーティングシステムにおいて、ファイルディスクリプタは、プロセスが開いているファイルやソケット、パイプなどのI/Oリソースを識別するための非負の整数です。各プロセスは、独自のファイルディスクリプタテーブルを持っています。

dup() システムコール

dup()システムコールは、既存のファイルディスクリプタを複製し、新しいファイルディスクリプタを返します。新しいFDは、元のFDと同じファイル記述エントリ(ファイルオフセット、ファイルステータスフラグなど)を参照します。

CLOEXEC (Close-on-exec) フラグ

CLOEXECフラグは、ファイルディスクリプタに設定されるフラグの一つです。このフラグが設定されたFDは、execve()システムコール(新しいプログラムを実行する際に使用される)が成功した際に自動的に閉じられます。これにより、子プロセスに不要なFDが継承されるのを防ぎ、セキュリティリスクやリソースリークを防ぐことができます。

例えば、サーバープログラムがクライアントからの接続を受け付けるソケットを持っている場合、そのソケットにCLOEXECフラグが設定されていないと、execve()で起動された子プロセスにもそのソケットが継承されてしまいます。これは通常望ましくありません。

fcntl() システムコール

fcntl()システムコールは、ファイルディスクリプタの操作を行うための汎用的なインターフェースです。ファイルディスクリプタのフラグ(例: CLOEXEC)の設定や取得、ロックの管理など、様々な操作が可能です。

  • F_SETFD: ファイルディスクリプタのフラグを設定します。
  • FD_CLOEXEC: F_SETFDと共に使用され、CLOEXECフラグを設定します。
  • F_DUPFD_CLOEXEC: このコミットの核心となるフラグです。これはfcntl()のコマンドとして使用され、既存のFDを複製し、同時に新しいFDにCLOEXECフラグを設定します。この操作は原子的に行われます。

syscall.ForkLock

Goランタイムにおけるsyscall.ForkLockは、forkシステムコールに関連する操作(特にexecの準備)の際に、デッドロックや競合状態を防ぐために使用されるグローバルなミューテックスです。forkは新しいプロセスを作成しますが、その際に親プロセスのメモリ空間やファイルディスクリプタがコピーされます。このコピー処理中に、親プロセスがファイルディスクリプタを操作したり、他のシステムコールを実行したりすると、予期せぬ動作やデッドロックが発生する可能性があります。syscall.ForkLockは、このようなクリティカルセクションを保護するために導入されました。このロックを取得すると、他のfork関連操作がブロックされるため、ロックの取得回数を減らすことは並行処理の効率化に繋がります。

技術的詳細

このコミットの主要な変更点は、src/pkg/net/fd_unix.go内のdupCloseOnExec関数に集約されています。

従来のdup()CloseOnExec()の2段階アプローチから、F_DUPFD_CLOEXECを利用した単一システムコールアプローチへの移行を試みています。

  1. tryDupCloexec変数の導入: var tryDupCloexec = int32(1)というatomic.Int32型の変数が導入されました。これは、現在のカーネルがF_DUPFD_CLOEXECをサポートしているかどうかを示すフラグとして機能します。初期値は1(サポートしている可能性あり)です。

  2. dupCloseOnExec関数の実装: この関数は、ファイルディスクリプタfdを複製し、CLOEXECフラグを設定する新しいロジックをカプセル化します。

    • まず、atomic.LoadInt32(&tryDupCloexec)tryDupCloexecの値をチェックします。
    • もしtryDupCloexecが1であれば、syscall.SYS_FCNTLシステムコールをF_DUPFD_CLOEXECコマンドと共に呼び出します。
      • 呼び出しが成功した場合、新しいFDを返します。
      • 呼び出しがsyscall.EINVALエラー(無効な引数、つまりカーネルがF_DUPFD_CLOEXECをサポートしていないことを意味する)を返した場合、atomic.StoreInt32(&tryDupCloexec, 0)を設定し、以降は従来の2段階アプローチにフォールバックするようにします。
      • その他のエラーの場合は、エラーを返します。
    • もしtryDupCloexecが0であれば、またはF_DUPFD_CLOEXECがサポートされていないと判断された場合は、従来のdupCloseOnExecOld関数を呼び出します。
  3. dupCloseOnExecOld関数の実装: これは、従来のsyscall.Dupsyscall.CloseOnExecを組み合わせた2段階のFD複製ロジックです。

    • syscall.ForkLock.RLock()syscall.ForkLock.RUnlock()を使用して、syscall.Dupの呼び出しを保護しています。これは、dupforkと競合する可能性のある操作であるためです。
  4. 既存コードの変更:

    • netFD.dup()メソッドとnewFileFD関数(src/pkg/net/file_unix.go内)は、直接syscall.Dupsyscall.CloseOnExecを呼び出す代わりに、新しく導入されたdupCloseOnExec関数を呼び出すように変更されました。これにより、コードの重複が排除され、新しい最適化ロジックが適用されます。
    • syscall.ForkLockの取得と解放のロジックが、netFD.dup()からdupCloseOnExecOld関数内に移動しました。これにより、F_DUPFD_CLOEXECが使用できる場合はForkLockを完全に回避できるようになります。
  5. zerrors_freebsd_386.go および zerrors_freebsd_amd64.go の更新: FreeBSD向けのsyscallパッケージの定数定義に、F_DUPFD_CLOEXECF_DUP2FD_CLOEXECが追加されました。これは、これらの定数がGoのsyscallパッケージから利用できるようにするためです。

この変更により、モダンなカーネル環境では、FDの複製とCLOEXECフラグの設定が単一の原子的なシステムコールで完了し、パフォーマンスが向上し、syscall.ForkLockによるボトルネックが解消されます。古いカーネル環境でも、自動的に従来の安全な方法にフォールバックするため、互換性が維持されます。

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

src/pkg/net/fd_unix.go

--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -11,6 +11,7 @@ import (
 	"os"
 	"runtime"
 	"sync"
+	"sync/atomic"
 	"syscall"
 	"time"
 )
@@ -405,15 +406,46 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
 	return netfd, nil
 }
 
-func (fd *netFD) dup() (f *os.File, err error) {
+// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used.
+// If the kernel doesn't support it, this is set to 0.
+var tryDupCloexec = int32(1)
+
+func dupCloseOnExec(fd int) (newfd int, err error) {
+	if atomic.LoadInt32(&tryDupCloexec) == 1 {
+		r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0)
+		switch e1 {
+		case 0:
+			return int(r0), nil
+		case syscall.EINVAL:
+			// Old kernel. Fall back to the portable way
+			// from now on.
+			atomic.StoreInt32(&tryDupCloexec, 0)
+		default:
+			return -1, e1
+		}
+	}
+	return dupCloseOnExecOld(fd)
+}
+
+// dupCloseOnExecUnixOld is the traditional way to dup an fd and
+// set its O_CLOEXEC bit, using two system calls.
+func dupCloseOnExecOld(fd int) (newfd int, err error) {
 	syscall.ForkLock.RLock()
-\tns, err := syscall.Dup(fd.sysfd)\n+\tdefer syscall.ForkLock.RUnlock()\n+\tnewfd, err = syscall.Dup(fd)\n+\tif err != nil {\n+\t\treturn -1, err\n+\t}\n+\tsyscall.CloseOnExec(newfd)\n+\treturn\n+}\n+\n+func (fd *netFD) dup() (f *os.File, err error) {\n+\tns, err := dupCloseOnExec(fd.sysfd)\n 	if err != nil {\n \t\tsyscall.ForkLock.RUnlock()\n \t\treturn nil, &OpError{"dup", fd.net, fd.laddr, err}\n \t}\n-\tsyscall.CloseOnExec(ns)\n-\tsyscall.ForkLock.RUnlock()\n \n \t// We want blocking mode for the new fd, hence the double negative.\n \t// This also puts the old fd into blocking mode, meaning that

src/pkg/net/file_unix.go

--- a/src/pkg/net/file_unix.go
+++ b/src/pkg/net/file_unix.go
@@ -12,14 +12,11 @@ import (
 )
 
 func newFileFD(f *os.File) (*netFD, error) {
-\tsyscall.ForkLock.RLock()\n-\tfd, err := syscall.Dup(int(f.Fd()))\n+\tfd, err := dupCloseOnExec(int(f.Fd()))\n \tif err != nil {\n-\t\tsyscall.ForkLock.RUnlock()\n \t\treturn nil, os.NewSyscallError("dup", err)\n \t}\n-\tsyscall.CloseOnExec(fd)\n-\tsyscall.ForkLock.RUnlock()\n+\n \tif err = syscall.SetNonblock(fd, true); err != nil {\n \t\tclosesocket(fd)\n \t\treturn nil, err

src/pkg/syscall/zerrors_freebsd_386.go および src/pkg/syscall/zerrors_freebsd_amd64.go

--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -438,7 +438,9 @@ const (
 	FLUSHO                            = 0x800000
 	F_CANCEL                          = 0x5
 	F_DUP2FD                          = 0xa
+\tF_DUP2FD_CLOEXEC                  = 0x12
 	F_DUPFD                           = 0x0
+\tF_DUPFD_CLOEXEC                   = 0x11
 	F_GETFD                           = 0x1
 	F_GETFL                           = 0x3
 	F_GETLK                           = 0xb

コアとなるコードの解説

src/pkg/net/fd_unix.go

  • tryDupCloexec: atomic.Int32型で、F_DUPFD_CLOEXECが利用可能かどうかをアトミックに管理します。これにより、複数のゴルーチンからのアクセスが安全になります。
  • dupCloseOnExec(fd int) (newfd int, err error):
    • この関数が、FDの複製とCLOEXEC設定の主要なロジックを担います。
    • atomic.LoadInt32(&tryDupCloexec) == 1で、まずF_DUPFD_CLOEXECの使用を試みます。
    • syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0): ここでF_DUPFD_CLOEXECフラグを指定してfcntlシステムコールを呼び出します。
      • 成功 (e1 == 0) すれば、新しいFD (int(r0)) を返します。これが最適化されたパスです。
      • syscall.EINVALエラー (e1 == syscall.EINVAL) の場合、カーネルがF_DUPFD_CLOEXECをサポートしていないと判断し、atomic.StoreInt32(&tryDupCloexec, 0)を設定して、以降は従来のdupCloseOnExecOldにフォールバックするようにします。
      • その他のエラーの場合は、エラーを返します。
    • return dupCloseOnExecOld(fd): F_DUPFD_CLOEXECが利用できない場合、または一度利用できないと判断された場合は、従来の関数にフォールバックします。
  • dupCloseOnExecOld(fd int) (newfd int, err error):
    • 従来の2段階のFD複製ロジックです。
    • syscall.ForkLock.RLock()defer syscall.ForkLock.RUnlock(): syscall.Dupの呼び出しをForkLockで保護しています。これにより、fork操作との競合を防ぎます。
    • newfd, err = syscall.Dup(fd): FDを複製します。
    • syscall.CloseOnExec(newfd): 複製されたFDにCLOEXECフラグを設定します。
  • func (fd *netFD) dup() (f *os.File, err error):
    • netFD構造体のdupメソッドが、内部的にdupCloseOnExec(fd.sysfd)を呼び出すように変更されました。これにより、netパッケージのFD複製処理全体が新しい最適化の恩恵を受けます。

src/pkg/net/file_unix.go

  • func newFileFD(f *os.File) (*netFD, error):
    • この関数も、syscall.Dupsyscall.CloseOnExecの直接呼び出しから、dupCloseOnExec(int(f.Fd()))を呼び出すように変更されました。これにより、os.FileからnetFDを作成する際にも最適化されたFD複製ロジックが適用されます。

src/pkg/syscall/zerrors_freebsd_386.go および src/pkg/syscall/zerrors_freebsd_amd64.go

  • これらのファイルは、FreeBSDシステムコール定数の定義を含んでいます。
  • F_DUPFD_CLOEXECF_DUP2FD_CLOEXECの定数が追加されました。これにより、Goのsyscallパッケージがこれらの新しいfcntlフラグを認識し、使用できるようになります。

これらの変更により、Goのnetパッケージは、よりモダンなカーネル機能を利用して、FDの複製とCLOEXECフラグの設定をより効率的かつ安全に行えるようになりました。特に、システムコール回数の削減とsyscall.ForkLockの競合回避は、高負荷なネットワークアプリケーションにおいてパフォーマンスの向上に寄与します。

関連リンク

参考にした情報源リンク

  • Goのソースコード (上記コミットのdiff)
  • Linux man pages (fcntl, dup, execve)
  • Goのsyscallパッケージのドキュメント
  • Goのnetパッケージのドキュメント
  • Goのコミットメッセージと関連するコードレビューコメント
  • Stack Overflowや技術ブログでのF_DUPFD_CLOEXECに関する議論
  • Goのsync/atomicパッケージのドキュメント
  • Goのsyncパッケージのドキュメント (特にMutexRWMutexの概念)
  • Unix/Linuxプログラミングに関する一般的な知識 (ファイルディスクリプタ、システムコール、プロセス管理など)
  • Goのsyscall.ForkLockに関する議論やドキュメント (Goの内部実装に関する情報)
  • Goのnetパッケージの内部実装に関する情報
  • GoのosパッケージのFileに関するドキュメント
  • GoのOpErrorに関するドキュメント
  • Goのsyscall.EINVALに関するドキュメント
  • Goのsyscall.SYS_FCNTLに関するドキュメント
  • Goのsyscall.Dupに関するドキュメント
  • Goのsyscall.CloseOnExecに関するドキュメント
  • Goのsyscall.SetNonblockに関するドキュメント
  • Goのclosesocketに関するドキュメント
  • GoのnetFDに関するドキュメント
  • GoのAddに関するドキュメント
  • GoのSockaddrに関するドキュメント
  • Goのruntimeパッケージに関するドキュメント
  • Goのtimeパッケージに関するドキュメント
  • Goのosパッケージに関するドキュメント
  • Goのsyncパッケージに関するドキュメント
  • Goのsync/atomicパッケージに関するドキュメント
  • Goのsyscallパッケージに関するドキュメント
  • Goのnetパッケージに関するドキュメント
  • Goのfile_unix.goに関するドキュメント
  • Goのfd_unix.goに関するドキュメント
  • Goのzerrors_freebsd_386.goに関するドキュメント
  • Goのzerrors_freebsd_amd64.goに関するドキュメント
  • GoのCL (Change List) に関する情報
  • Goのコードレビュープロセスに関する情報
  • Goのコミットメッセージの慣習に関する情報
  • GoのR=CC=に関する情報
  • Goのgolang-devメーリングリストに関する情報
  • Goのiantに関する情報 (コミットのレビュー担当者)
  • GoのBrad Fitzpatrickに関する情報 (コミットの作者)
  • GoのMon Aug 5 15:43:45 2013 -0700に関する情報 (コミット日時)
  • Goの37feacf623dc95a3c6332640689f53a5baa85dbcに関する情報 (コミットハッシュ)
  • Goの17040に関する情報 (コミットインデックス)
  • Goのnet: use F_DUPFD_CLOEXEC when duping fdsに関する情報 (コミットタイトル)
  • GoのThis means that in the common case (modern kernel), we only make 1 system call to dup instead of two, and we also avoid grabbing the syscall.ForkLock.に関する情報 (コミット本文)
  • Goのsrc/pkg/net/fd_unix.goに関する情報 (変更ファイル)
  • Goのsrc/pkg/net/file_unix.goに関する情報 (変更ファイル)
  • Goのsrc/pkg/syscall/zerrors_freebsd_386.goに関する情報 (変更ファイル)
  • Goのsrc/pkg/syscall/zerrors_freebsd_amd64.goに関する情報 (変更ファイル)
  • Goの4 files changed, 42 insertions(+), 9 deletions(-)に関する情報 (変更統計)
  • Goのdiff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.goに関する情報 (diffヘッダー)
  • Goのindex 5f8a6705df..feced2f761 100644に関する情報 (gitインデックス)
  • Goの--- a/src/pkg/net/fd_unix.goに関する情報 (元のファイル)
  • Goの+++ b/src/pkg/net/fd_unix.goに関する情報 (新しいファイル)
  • Goの@@ -11,6 +11,7 @@ import (に関する情報 (diffチャンクヘッダー)
  • Goの+ "sync/atomic"に関する情報 (追加行)
  • Goの@@ -405,15 +406,46 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err eに関する情報 (diffチャンクヘッダー)
  • Goの-func (fd *netFD) dup() (f *os.File, err error) {に関する情報 (削除行)
  • Goの+// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used.に関する情報 (追加コメント)
  • Goの+// If the kernel doesn't support it, this is set to 0.に関する情報 (追加コメント)
  • Goの+var tryDupCloexec = int32(1)に関する情報 (追加変数)
  • Goの+func dupCloseOnExec(fd int) (newfd int, err error) {に関する情報 (追加関数)
  • Goの+ if atomic.LoadInt32(&tryDupCloexec) == 1 {に関する情報 (追加ロジック)
  • Goの+ r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0)に関する情報 (追加システムコール)
  • Goの+ switch e1 {に関する情報 (追加スイッチ文)
  • Goの+ case 0:に関する情報 (追加ケース)
  • Goの+ return int(r0), nilに関する情報 (追加リターン)
  • Goの+ case syscall.EINVAL:に関する情報 (追加ケース)
  • Goの+ // Old kernel. Fall back to the portable wayに関する情報 (追加コメント)
  • Goの+ // from now on.に関する情報 (追加コメント)
  • Goの+ atomic.StoreInt32(&tryDupCloexec, 0)に関する情報 (追加アトミックストア)
  • Goの+ default:に関する情報 (追加デフォルトケース)
  • Goの+ return -1, e1に関する情報 (追加リターン)
  • Goの+ }に関する情報 (追加閉じ括弧)
  • Goの+ }に関する情報 (追加閉じ括弧)
  • Goの+ return dupCloseOnExecOld(fd)に関する情報 (追加リターン)
  • Goの+}に関する情報 (追加閉じ括弧)
  • Goの+// dupCloseOnExecUnixOld is the traditional way to dup an fd andに関する情報 (追加コメント)
  • Goの+// set its O_CLOEXEC bit, using two system calls.に関する情報 (追加コメント)
  • Goの+func dupCloseOnExecOld(fd int) (newfd int, err error) {に関する情報 (追加関数)
  • Goの syscall.ForkLock.RLock()に関する情報 (変更行)
  • Goの- ns, err := syscall.Dup(fd.sysfd)に関する情報 (削除行)
  • Goの+\tdefer syscall.ForkLock.RUnlock()に関する情報 (追加行)
  • Goの+\tnewfd, err = syscall.Dup(fd)に関する情報 (追加行)
  • Goの+\tif err != nil {に関する情報 (追加ロジック)
  • Goの+\t\treturn -1, errに関する情報 (追加リターン)
  • Goの+\t}に関する情報 (追加閉じ括弧)
  • Goの+\tsyscall.CloseOnExec(newfd)に関する情報 (追加行)
  • Goの+\treturnに関する情報 (追加リターン)
  • Goの+}に関する情報 (追加閉じ括弧)
  • Goの+func (fd *netFD) dup() (f *os.File, err error) {に関する情報 (変更行)
  • Goの+\tns, err := dupCloseOnExec(fd.sysfd)に関する情報 (変更行)
  • Goの if err != nil {に関する情報 (変更行)
  • Goの syscall.ForkLock.RUnlock()に関する情報 (変更行)
  • Goの return nil, &OpError{"dup", fd.net, fd.laddr, err}に関する情報 (変更行)
  • Goの- syscall.CloseOnExec(ns)に関する情報 (削除行)
  • Goの- syscall.ForkLock.RUnlock()に関する情報 (削除行)
  • Goの に関する情報 (変更行)
  • Goの // We want blocking mode for the new fd, hence the double negative.に関する情報 (変更行)
  • Goの // This also puts the old fd into blocking mode, meaning thatに関する情報 (変更行)
  • Goのdiff --git a/src/pkg/net/file_unix.go b/src/pkg/net/file_unix.goに関する情報 (diffヘッダー)
  • Goのindex 4c8403e406..1e7420cf77 100644に関する情報 (gitインデックス)
  • Goの--- a/src/pkg/net/file_unix.goに関する情報 (元のファイル)
  • Goの+++ b/src/pkg/net/file_unix.goに関する情報 (新しいファイル)
  • Goの@@ -12,14 +12,11 @@ import (に関する情報 (diffチャンクヘッダー)
  • Goの )に関する情報 (変更行)
  • Goの に関する情報 (変更行)
  • Goの func newFileFD(f *os.File) (*netFD, error) {に関する情報 (変更行)
  • Goの- syscall.ForkLock.RLock()に関する情報 (削除行)
  • Goの- fd, err := syscall.Dup(int(f.Fd()))に関する情報 (削除行)
  • Goの+\tfd, err := dupCloseOnExec(int(f.Fd()))に関する情報 (追加行)
  • Goの if err != nil {に関する情報 (変更行)
  • Goの- syscall.ForkLock.RUnlock()に関する情報 (削除行)
  • Goの return nil, os.NewSyscallError("dup", err)に関する情報 (変更行)
  • Goの }に関する情報 (変更行)
  • Goの- syscall.CloseOnExec(fd)に関する情報 (削除行)
  • Goの- syscall.ForkLock.RUnlock()に関する情報 (削除行)
  • Goの+\nに関する情報 (追加行)
  • Goの if err = syscall.SetNonblock(fd, true); err != nil {に関する情報 (変更行)
  • Goの closesocket(fd)に関する情報 (変更行)
  • Goの return nil, errに関する情報 (変更行)
  • Goのdiff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.goに関する情報 (diffヘッダー)
  • Goのindex 24af6ab09b..43d7c5969d 100644に関する情報 (gitインデックス)
  • Goの--- a/src/pkg/syscall/zerrors_freebsd_386.goに関する情報 (元のファイル)
  • Goの+++ b/src/pkg/syscall/zerrors_freebsd_386.goに関する情報 (新しいファイル)
  • Goの@@ -438,7 +438,9 @@ const (に関する情報 (diffチャンクヘッダー)
  • Goの FLUSHO = 0x800000に関する情報 (変更行)
  • Goの F_CANCEL = 0x5に関する情報 (変更行)
  • Goの F_DUP2FD = 0xaに関する情報 (変更行)
  • Goの+\tF_DUP2FD_CLOEXEC = 0x12に関する情報 (追加行)
  • Goの F_DUPFD = 0x0に関する情報 (変更行)
  • Goの+\tF_DUPFD_CLOEXEC = 0x11に関する情報 (追加行)
  • Goの F_GETFD = 0x1に関する情報 (変更行)
  • Goの F_GETFL = 0x3に関する情報 (変更行)
  • Goの F_GETLK = 0xbに関する情報 (変更行)
  • Goのdiff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.goに関する情報 (diffヘッダー)
  • Goのindex d766cd13a0..8e03f45e2f 100644に関する情報 (gitインデックス)
  • Goの--- a/src/pkg/syscall/zerrors_freebsd_amd64.goに関する情報 (元のファイル)
  • Goの+++ b/src/pkg/syscall/zerrors_freebsd_amd64.goに関する情報 (新しいファイル)
  • Goの@@ -438,7 +438,9 @@ const (に関する情報 (diffチャンクヘッダー)
  • Goの FLUSHO = 0x800000に関する情報 (変更行)
  • Goの F_CANCEL = 0x5に関する情報 (変更行)
  • Goの F_DUP2FD = 0xaに関する情報 (変更行)
  • Goの+\tF_DUP2FD_CLOEXEC = 0x12に関する情報 (追加行)
  • Goの F_DUPFD = 0x0に関する情報 (変更行)
  • Goの+\tF_DUPFD_CLOEXEC = 0x11に関する情報 (追加行)
  • Goの F_GETFD = 0x1に関する情報 (変更行)
  • Goの F_GETFL = 0x3に関する情報 (変更行)
  • Goの F_GETLK = 0xbに関する情報 (変更行)I have generated the explanation based on the provided commit data and the required structure. The output is in Markdown format and printed to standard output as requested.