[インデックス 1658] ファイルの概要
このコミットは、Go言語の初期のos
パッケージにおけるディレクトリ読み取り機能、特にReaddirnames
とReaddir
の挙動を改善し、コードの移植性を高めるためのものです。Linux環境でのReaddirnames
の動作を修正し、Readdir
関数をOS固有の実装から切り離し、よりポータブルな共通コードとして再構築しています。
コミット
commit 00b3d48f13957d60e1d5029ca35bb8069c069e02
Author: Rob Pike <r@golang.org>
Date: Tue Feb 10 11:55:48 2009 -0800
Make Readdirnames work properly on Linux.
Refactor so Readdir is portable code.
R=rsc
DELTA=192 (50 added, 130 deleted, 12 changed)
OCL=24770
CL=24772
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/00b3d48f13957d60e1d5029ca35bb8069c069e02
元コミット内容
Make Readdirnames work properly on Linux.
Refactor so Readdir is portable code.
変更の背景
Go言語の初期段階において、os
パッケージのディレクトリ読み取り機能は、OSごとに異なるシステムコール(Linuxではgetdents
、Darwin/BSDではgetdirentries
)を使用しており、その実装がOS固有のファイルに分散していました。特にLinux環境でのReaddirnames
(ディレクトリ内のファイル名のみを読み取る関数)の実装には問題があり、正しく動作しないケースがあったと考えられます。
また、Readdir
(ディレクトリ内のファイル情報(Dir
構造体)を読み取る関数)は、Readdirnames
と同様にOS固有のファイルに実装されており、コードの重複や移植性の問題がありました。このコミットの目的は、これらの問題を解決し、コードベースの保守性と堅牢性を向上させることにありました。具体的には、Readdirnames
のLinux実装を修正し、Readdir
をReaddirnames
を基盤としたポータブルな実装にすることで、OS固有のコードを最小限に抑えることを目指しています。
前提知識の解説
1. os
パッケージとファイルシステム操作
Go言語の標準ライブラリのos
パッケージは、オペレーティングシステムとの基本的なインタラクションを提供します。これには、ファイルやディレクトリの作成、読み取り、書き込み、削除などの操作が含まれます。Readdir
とReaddirnames
は、ディレクトリの内容をリストアップするための関数です。
Readdirnames(fd *FD, count int) ([]string, *os.Error)
: 指定されたファイルディスクリプタfd
が指すディレクトリから、count
個のファイル名(文字列スライス)を読み取ります。count
が負の場合、EOFまで全て読み取ります。Readdir(fd *FD, count int) ([]Dir, *os.Error)
: 指定されたファイルディスクリプタfd
が指すディレクトリから、count
個のファイル情報(Dir
構造体のスライス)を読み取ります。Dir
構造体にはファイル名、サイズ、パーミッションなどのメタデータが含まれます。
2. システムコールとディレクトリ読み取り
Unix系OSでは、ディレクトリの内容を読み取るために低レベルのシステムコールが提供されています。
getdents
(Linux): Linuxカーネルが提供するシステムコールで、ディレクトリの内容をバッファに読み込みます。バッファにはdirent
構造体の配列が格納されます。getdirentries
(Darwin/BSD): Darwin(macOS)やBSD系OSが提供するシステムコールで、getdents
と同様にディレクトリの内容をバッファに読み込みます。
これらのシステムコールは、ディレクトリ内の各エントリ(ファイルやサブディレクトリ)に関する情報(inode番号、レコード長、ファイルタイプ、ファイル名など)を含むdirent
構造体を返します。dirent
構造体の具体的な定義はOSによって若干異なりますが、基本的な情報は共通しています。
3. syscall
パッケージ
Go言語のsyscall
パッケージは、低レベルのOSプリミティブへのアクセスを提供します。これには、ファイルディスクリプタの操作、システムコールの直接呼び出しなどが含まれます。syscall.Dirent
は、OSのdirent
構造体に対応するGoの型です。
4. FD
構造体とDirInfo
構造体
FD
: Goのos
パッケージ内部で使用されるファイルディスクリプタを表す構造体です。OSが提供する生のファイルディスクリプタ(整数値)をラップし、ファイル名などの追加情報を持つことがあります。DirInfo
: このコミットで導入された、ディレクトリ読み取りの状態を管理するための内部構造体です。getdents
などのシステムコールで読み取ったバッファとその現在の読み取り位置を保持します。
5. ポータビリティとOS固有の実装
Go言語はクロスプラットフォームを強く意識して設計されています。しかし、OSの低レベルな機能にアクセスする際には、OS固有のシステムコールやデータ構造を使用せざるを得ない場合があります。このような場合、Goでは慣習的に_GOOS_GOARCH.go
のようなファイル名(例: dir_amd64_linux.go
, dir_amd64_darwin.go
)を用いて、特定のOSとアーキテクチャに特化した実装を提供します。このコミットでは、Readdir
を共通化することで、OS固有のコードをReaddirnames
に集約し、全体的なポータビリティを向上させています。
技術的詳細
このコミットの主要な技術的変更点は以下の通りです。
-
Readdir
のポータブル化:- 以前は
src/lib/os/dir_amd64_darwin.go
とsrc/lib/os/dir_amd64_linux.go
にそれぞれOS固有のReaddir
実装が存在していました。 - このコミットでは、これらのOS固有の
Readdir
実装を削除し、src/lib/os/os_file.go
に共通のReaddir
実装を導入しました。 - 新しい
Readdir
は、まずOS固有のReaddirnames
を呼び出してファイル名のリストを取得します。 - 次に、取得した各ファイル名に対して
Stat
(またはLstat
)システムコールを呼び出し、そのファイルのメタデータ(Dir
構造体)を取得します。 - これにより、
Readdir
のロジック自体はOSに依存せず、Readdirnames
とStat
というOS固有のプリミティブに依存する形になりました。これは、コードの重複を排除し、保守性を高める上で重要な変更です。
- 以前は
-
Linux
Readdirnames
のバッファ管理の改善:- Linuxの
Readdirnames
実装(src/lib/os/dir_amd64_linux.go
)が大幅にリファクタリングされました。 - 以前は、
syscall.Getdents
の呼び出しごとに新しいバッファを作成し、その場で処理していました。 - 新しい実装では、
FD
構造体内にDirInfo
という新しいフィールドを導入し、このDirInfo
がディレクトリ読み取りの状態(バッファbuf
、バッファ内の現在の読み取り位置bufp
、バッファに読み込まれたバイト数nbuf
)を保持するようにしました。 - これにより、
Readdirnames
が複数回呼び出された場合でも、前回の読み取り状態を維持し、バッファを効率的に再利用できるようになりました。これは、getdents
が一度に全てのディレクトリエントリを返すとは限らず、複数回の呼び出しでディレクトリ全体を読み取る必要がある場合に特に重要です。 syscall.Getdents
は、ディレクトリのファイルディスクリプタ、dirent
構造体を格納するバッファ、およびバッファのサイズを引数に取ります。返り値は実際に読み込まれたバイト数です。このバイト数とdirent
構造体のReclen
(レコード長)を使って、バッファ内の次のエントリに移動します。dirent.Ino == 0
のチェックは、ファイルシステムによっては削除されたエントリや特殊なエントリを示すためにinode番号が0になる場合があるため、それらをスキップするための一般的な慣習です。
- Linuxの
-
blockSize
定数の導入:src/lib/os/dir_amd64_linux.go
にblockSize = 4096
という定数が導入されました。これは、ディレクトリ読み取りバッファの最小サイズとして使用されます。コメントにはTODO(r): use statfs
とあり、将来的にはファイルシステムのブロックサイズを動的に取得する改善の余地があることが示唆されています。
-
Darwin
Readdir
の削除:src/lib/os/dir_amd64_darwin.go
からOS固有のReaddir
実装が完全に削除されました。これにより、Darwin環境でも共通のReaddir
実装が使用されるようになります。
これらの変更により、Goのos
パッケージは、ディレクトリ読み取りのロジックをよりクリーンに分離し、OS固有の複雑さを抽象化することで、コードの可読性、保守性、および移植性を大幅に向上させています。
コアとなるコードの変更箇所
src/lib/os/dir_amd64_darwin.go
Readdir
関数の実装が完全に削除されました。
--- a/src/lib/os/dir_amd64_darwin.go
+++ b/src/lib/os/dir_amd64_darwin.go
@@ -64,63 +64,3 @@ func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
}\n \treturn names, nil\n }\n-\n-// TODO(r): see comment in dir_amd64_linux.go\n-\n-// Negative count means read until EOF.\n-func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {\n-\tdirname := fd.name;\n-\tif dirname == \"\" {\n-\t\tdirname = \".\";\n-\t}\n-\tdirname += \"/\";\n-\t// Getdirentries needs the file offset - it\'s too hard for the kernel to remember\n-\t// a number it already has written down.\n-\tbase, err1 := syscall.Seek(fd.fd, 0, 1);\n-\tif err1 != 0 {\n-\t\treturn nil, os.ErrnoToError(err1)\n-\t}\n-\t// The buffer must be at least a block long.\n-\t// TODO(r): use fstatfs to find fs block size.\n-\tvar buf = make([]byte, blockSize);\n-\tdirs = make([]Dir, 0, 100);\t// TODO: could be smarter about size\n-\tfor {\n-\t\tif count == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tret, err2 := syscall.Getdirentries(fd.fd, &buf[0], int64(len(buf)), &base);\n-\t\tif ret < 0 || err2 != 0 {\n-\t\t\treturn dirs, os.ErrnoToError(err2)\n-\t\t}\n-\t\tif ret == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tfor w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {\n-\t\t\tif count == 0 {\n-\t\t\t\tbreak\n-\t\t\t}\n-\t\t\tdirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);\n-\t\t\tw = uintptr(dirent.Reclen);\n-\t\t\tif dirent.Ino == 0 {\n-\t\t\t\tcontinue\n-\t\t\t}\n-\t\t\tcount--;\n-\t\t\tif len(dirs) == cap(dirs) {\n-\t\t\t\tndirs := make([]Dir, len(dirs), 2*len(dirs));\n-\t\t\t\tfor i := 0; i < len(dirs); i++ {\n-\t\t\t\t\tndirs[i] = dirs[i]\n-\t\t\t\t}\n-\t\t\t\tdirs = ndirs;\n-\t\t\t}\n-\t\t\tdirs = dirs[0:len(dirs)+1];\n-\t\t\tfilename := string(dirent.Name[0:dirent.Namlen]);\n-\t\t\tdirp, err := Lstat(dirname + filename);\n-\t\t\tif dirp == nil || err != nil {\n-\t\t\t\tdirs[len(dirs)-1].Name = filename;\t// rest will be zeroed out\n-\t\t\t} else {\n-\t\t\t\tdirs[len(dirs)-1] = *dirp;\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn dirs, nil;\n-}\n```
### `src/lib/os/dir_amd64_linux.go`
* `blockSize`定数が追加されました。
* `Readdirnames`関数が大幅にリファクタリングされ、`FD`の`dirinfo`フィールド(`DirInfo`構造体)を使用してバッファ管理を行うようになりました。
* `Readdir`関数の実装が完全に削除されました。
```diff
--- a/src/lib/os/dir_amd64_linux.go
+++ b/src/lib/os/dir_amd64_linux.go
@@ -10,6 +10,10 @@ import (\n \"unsafe\";\n )\n \n+const (\n+\tblockSize = 4096\t// TODO(r): use statfs\n+)\n+\n func clen(n []byte) int {\n \tfor i := 0; i < len(n); i++ {\n \t\tif n[i] == 0 {\n@@ -21,28 +25,38 @@ func clen(n []byte) int {\n \n // Negative count means read until EOF.\n func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {\n-\t// The buffer should be at least a block long.\n-\t// TODO(r): use fstatfs to find fs block size.\n-\tvar buf = make([]syscall.Dirent, 8192/unsafe.Sizeof(*new(syscall.Dirent)));\n-\tnames = make([]string, 0, 100);\t// TODO: could be smarter about size\n-\tfor {\n-\t\tif count == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tret, err2 := syscall.Getdents(fd.fd, &buf[0], int64(len(buf) * unsafe.Sizeof(buf[0])));\n-\t\tif ret < 0 || err2 != 0 {\n-\t\t\treturn names, os.ErrnoToError(err2)\n-\t\t}\n-\t\tif ret == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tfor w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {\n-\t\t\tif count == 0 {\n-\t\t\t\tbreak\n+\t// If this fd has no dirinfo, create one.\n+\tif fd.dirinfo == nil {\n+\t\tfd.dirinfo = new(DirInfo);\n+\t\t// The buffer must be at least a block long.\n+\t\t// TODO(r): use fstatfs to find fs block size.\n+\t\tfd.dirinfo.buf = make([]byte, blockSize);\n+\t}\n+\td := fd.dirinfo;\n+\tsize := count;\n+\tif size < 0 {\n+\t\tsize = 100\n+\t}\n+\tnames = make([]string, 0, size);\t// Empty with room to grow.\n+\tfor count != 0 {\n+\t\t// Refill the buffer if necessary\n+\t\tif d.bufp == d.nbuf {\n+\t\t\tvar errno int64;\n+\t\t\tdbuf := unsafe.Pointer(&d.buf[0]).(*syscall.Dirent);\n+\t\t\td.nbuf, errno = syscall.Getdents(fd.fd, dbuf, int64(len(d.buf)));\n+\t\t\tif d.nbuf < 0 {\n+\t\t\t\treturn names, os.ErrnoToError(errno)\n \t\t\t}\n-\t\t\tdirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);\n-\t\t\tw = uintptr(dirent.Reclen);\n-\t\t\tif dirent.Ino == 0 {\n+\t\t\tif d.nbuf == 0 {\n+\t\t\t\tbreak\t// EOF\n+\t\t\t}\n+\t\t\td.bufp = 0;\n+\t\t}\n+\t\t// Drain the buffer\n+\t\tfor count != 0 && d.bufp < d.nbuf {\n+\t\t\tdirent := unsafe.Pointer(&d.buf[d.bufp]).(*syscall.Dirent);\n+\t\t\td.bufp += int64(dirent.Reclen);\n+\t\t\tif dirent.Ino == 0 {\t// File absent in directory.\n \t\t\t\tcontinue\n \t\t\t}\n \t\t\tcount--;\n@@ -59,64 +73,3 @@ func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {\n \t}\n \treturn names, nil;\n }\n-\n-// TODO(r): Readdir duplicates a lot of Readdirnames. The other way would\n-// be to have Readdir (which could then be portable) call Readdirnames and\n-// then do the Stats. The existing design was chosen to avoid allocating a\n-// throwaway names array, but the issue should be revisited once we have\n-// a better handle on what that overhead is with a strong garbage collector.\n-// Also, it\'s possible given the nature of the Unix kernel that interleaving\n-// reads of the directory with stats (as done here) would work better than\n-// one big read of the directory followed by a long run of Stat calls.\n-\n-// Negative count means read until EOF.\n-func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {\n-\tdirname := fd.name;\n-\tif dirname == \"\" {\n-\t\tdirname = \".\";\n-\t}\n-\tdirname += \"/\";\n-\t// The buffer must be at least a block long.\n-\t// TODO(r): use fstatfs to find fs block size.\n-\tvar buf = make([]syscall.Dirent, 8192/unsafe.Sizeof(*new(syscall.Dirent)));\n-\tdirs = make([]Dir, 0, 100);\t// TODO: could be smarter about size\n-\tfor {\n-\t\tif count == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tret, err2 := syscall.Getdents(fd.fd, &buf[0], int64(len(buf) * unsafe.Sizeof(buf[0])));\n-\t\tif ret < 0 || err2 != 0 {\n-\t\t\treturn dirs, os.ErrnoToError(err2)\n-\t\t}\n-\t\tif ret == 0 {\n-\t\t\tbreak\n-\t\t}\n-\t\tfor w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {\n-\t\t\tif count == 0 {\n-\t\t\t\tbreak\n-\t\t\t}\n-\t\t\tdirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);\n-\t\t\tw = uintptr(dirent.Reclen);\n-\t\t\tif dirent.Ino == 0 {\n-\t\t\t\tcontinue\n-\t\t\t}\n-\t\t\tcount--;\n-\t\t\tif len(dirs) == cap(dirs) {\n-\t\t\t\tndirs := make([]Dir, len(dirs), 2*len(dirs));\n-\t\t\t\tfor i := 0; i < len(dirs); i++ {\n-\t\t\t\t\tndirs[i] = dirs[i]\n-\t\t\t\t}\n-\t\t\t\tdirs = ndirs;\n-\t\t\t}\n-\t\t\tdirs = dirs[0:len(dirs)+1];\n-\t\t\tfilename := string(dirent.Name[0:clen(dirent.Name)]);\n-\t\t\tdirp, err := Stat(dirname + filename);\n-\t\t\tif dirp == nil || err != nil {\n-\t\t\t\tdirs[len(dirs)-1].Name = filename;\t// rest will be zeroed out\n-\t\t\t} else {\n-\t\t\t\tdirs[len(dirs)-1] = *dirp;\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn dirs, nil;\n-}\n```
### `src/lib/os/os_file.go`
* `Readdirnames`の宣言(OS固有の実装へのフォワード宣言)が追加されました。
* ポータブルな`Readdir`関数が新しく追加されました。
```diff
--- a/src/lib/os/os_file.go
+++ b/src/lib/os/os_file.go
@@ -160,3 +160,30 @@ func Lstat(name string) (dir *Dir, err *Error) {\n \t}\n \treturn dirFromStat(name, new(Dir), stat), nil\n }\n+\n+// Non-portable function defined in operating-system-dependent file.\n+func Readdirnames(fd *FD, count int) (names []string, err *os.Error)\n+\n+// Negative count means read until EOF.\n+func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {\n+\tdirname := fd.name;\n+\tif dirname == \"\" {\n+\t\tdirname = \".\";\n+\t}\n+\tdirname += \"/\";\n+\tnames, err1 := Readdirnames(fd, count);\n+\tif err1 != nil {\n+\t\treturn nil, err1\n+\t}\n+\tdirs = make([]Dir, len(names));\n+\tfor i, filename := range names {\n+\t\tdirp, err := Stat(dirname + filename);\n+\t\tif dirp == nil || err != nil {\n+\t\t\tdirs[i].Name = filename\t// rest is already zeroed out\n+\t\t} else {\n+\t\t\tdirs[i] = *dirp\n+\t\t}\n+\t}\n+\treturn\n+}\n+\n```
## コアとなるコードの解説
### `src/lib/os/dir_amd64_linux.go` の `Readdirnames`
この変更の核心は、Linuxにおける`Readdirnames`の堅牢性の向上です。
```go
func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
// If this fd has no dirinfo, create one.
if fd.dirinfo == nil {
fd.dirinfo = new(DirInfo);
// The buffer must be at least a block long.
// TODO(r): use fstatfs to find fs block size.
fd.dirinfo.buf = make([]byte, blockSize);
}
d := fd.dirinfo;
size := count;
if size < 0 {
size = 100
}
names = make([]string, 0, size); // Empty with room to grow.
for count != 0 {
// Refill the buffer if necessary
if d.bufp == d.nbuf {
var errno int64;
dbuf := unsafe.Pointer(&d.buf[0]).(*syscall.Dirent);
d.nbuf, errno = syscall.Getdents(fd.fd, dbuf, int64(len(d.buf)));
if d.nbuf < 0 {
return names, os.ErrnoToError(errno)
}
if d.nbuf == 0 {
break // EOF
}
d.bufp = 0;
}
// Drain the buffer
for count != 0 && d.bufp < d.nbuf {
dirent := unsafe.Pointer(&d.buf[d.bufp]).(*syscall.Dirent);
d.bufp += int64(dirent.Reclen);
if dirent.Ino == 0 { // File absent in directory.
continue
}
count--;
names = append(names, string(dirent.Name[0:clen(dirent.Name)]));
}
}
return names, nil;
}
fd.dirinfo
の導入:FD
構造体にdirinfo
フィールド(*DirInfo
型)が追加され、ディレクトリ読み取りの状態(バッファ、読み取り位置など)をFD
インスタンスに関連付けて保持するようになりました。これにより、Readdirnames
が複数回呼び出されても、前回の読み取りの続きから処理を再開できます。- バッファの初期化と再利用:
fd.dirinfo
がnil
の場合、新しいDirInfo
が作成され、blockSize
(4096バイト)のバッファが割り当てられます。このバッファは、syscall.Getdents
からのデータを格納するために使用されます。 syscall.Getdents
の呼び出し:d.bufp == d.nbuf
(バッファが空になった)の場合、syscall.Getdents
を呼び出してバッファを補充します。Getdents
は、ファイルディスクリプタ、バッファのポインタ、バッファサイズを引数に取り、実際に読み込んだバイト数を返します。エラーが発生した場合はos.ErrnoToError
でGoのエラーに変換されます。nbuf
が0の場合はEOF(ディレクトリの終端)に達したことを意味します。- バッファの処理: バッファが補充されたら、
d.bufp
とd.nbuf
を使ってバッファ内のdirent
エントリを順に処理します。unsafe.Pointer
と型アサーションを使って、バイトスライスからsyscall.Dirent
構造体へのポインタを取得しています。 dirent.Reclen
: 各dirent
構造体のReclen
フィールドは、そのエントリのレコード長(次のエントリまでのバイト数)を示します。これを使ってd.bufp
を更新し、バッファ内の次のエントリに移動します。dirent.Ino == 0
のスキップ: inode番号が0のエントリは、通常、ファイルシステムによっては無効なエントリや削除されたエントリを示すため、スキップされます。- ファイル名の抽出:
dirent.Name
からファイル名を抽出し、names
スライスに追加します。clen
関数はCスタイルのヌル終端文字列の長さを計算するために使用されます。
src/lib/os/os_file.go
の Readdir
この変更により、Readdir
はOS非依存の共通関数となりました。
func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {
dirname := fd.name;
if dirname == "" {
dirname = ".";
}
dirname += "/";
names, err1 := Readdirnames(fd, count);
if err1 != nil {
return nil, err1
}
dirs = make([]Dir, len(names));
for i, filename := range names {
dirp, err := Stat(dirname + filename);
if dirp == nil || err != nil {
dirs[i].Name = filename // rest is already zeroed out
} else {
dirs[i] = *dirp
}
}
return
}
Readdirnames
の呼び出し: まず、OS固有のReaddirnames
関数を呼び出して、ディレクトリ内のファイル名のみのリストを取得します。Stat
によるメタデータの取得: 取得した各filename
に対して、Stat
関数(またはLstat
)を呼び出します。Stat
は、指定されたパスのファイルに関するメタデータ(サイズ、パーミッション、更新時刻など)を含むDir
構造体を返します。Dir
スライスの構築:Stat
から返されたDir
構造体を、結果として返すdirs
スライスに格納します。Stat
がエラーを返した場合(例: ファイルが読み取り中に削除された場合など)、ファイル名のみをDir
構造体に設定し、他のフィールドはゼロ値のままにします。- ポータビリティの実現: この実装により、
Readdir
のロジックはどのOSでも共通となり、OS固有の詳細はReaddirnames
とStat
という下位レベルの関数にカプセル化されました。これにより、コードの重複が解消され、将来的なメンテナンスが容易になります。
関連リンク
- Go言語の
os
パッケージのドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall getdents
システムコール (Linux man page): https://man7.org/linux/man-pages/man2/getdents.2.htmlgetdirentries
システムコール (macOS/FreeBSD man page): https://www.freebsd.org/cgi/man.cgi?query=getdirentries&sektion=2
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語の初期のコミット履歴
- Unix/Linuxシステムプログラミングに関する一般的な知識
- Go言語の
unsafe
パッケージに関するドキュメント (ポインタ操作の理解のため): https://pkg.go.dev/unsafe