---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: 純粋な Go による syscall.Syscall と unsafe.Pointer を用いた Linux システムコールの直接呼び出し
description: cgoを使用せず、ABI に整合した構造体、unsafe.Pointer、および標準の syscall パッケージを用いて、純粋な Go から SYS_SYSINFO などの Linux カーネルのシステムコールを直接呼び出すこと。
language: ja
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## 概要

Go における直接的なシステムコールについて学びます。
Go は厳格なコンパイラエラーと堅牢な GC を提供しているため、低レベルの呼び出しを純粋な Go で置き換えることがはるかに望ましいです。
幸いなことに、ほとんどの C の関数呼び出しは、優れた現代的な方法で Go に完全に再実装されています。
それでは見ていきましょう。

## システムコール

システムコールとは、オペレーティングシステムへの直接的な要求です。
システムはハードウェア上で直接動作するため、通常は堅苦しく古風な形式で書かれており、その呼び出しは厳密かつ正確な形式の要求でなければならないことを考慮する必要があります。
したがって、変数を使用しない場合でも、使用の有無にかかわらずサイズを埋める必要があります。
完全に動作する例で確認してみましょう。

## 完全な例

```go
package main
import (
	"fmt"
	"syscall"
	"unsafe"
)

type sysinfo_t struct {
	Uptime    int64
	Loads     [3]uint64
	Totalram  uint64
	Freeram   uint64
	Sharedram uint64
	Bufferram uint64
	Totalswap uint64
	Freeswap  uint64
	Procs     uint16
	Pad       uint16
	_         [4]byte
	Totalhigh uint64
	Freehigh  uint64
	MemUnit   uint32
	_         [4]byte
}

func main() {
	var info sysinfo_t
	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
	if errno != 0 {
		fmt.Println("sysinfo syscall failed:", errno)
		return
	}

	scale := float64(1 << 16)
	fmt.Printf("Uptime: %d seconds\n", info.Uptime)
	fmt.Printf("Load Average: %.2f %.2f %.2f\n",
		float64(info.Loads[0])/scale,
		float64(info.Loads[1])/scale,
		float64(info.Loads[2])/scale)
	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
		info.Totalram*uint64(info.MemUnit)/1024/1024,
		info.Freeram*uint64(info.MemUnit)/1024/1024,
		info.Bufferram*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Swap: total=%d MB free=%d MB\n",
		info.Totalswap*uint64(info.MemUnit)/1024/1024,
		info.Freeswap*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Processes: %d\n", info.Procs)
}
```

この例はすべての変数を含んでおり、現在のシステム情報を詳細に出力します。
このコードは、錠前と鍵になぞらえて考えることができます。
`syscall.SYS_SYSINFO` は、カーネル内部にある錠前を開ける鍵です。
したがって、錠前に対して正しい鍵を使用することが重要です。
この呼び出しに `syscall.SYS_GETPID` を使用するとどうなるでしょうか。
これはプロセス ID を含む錠前の鍵です。
これは、システム情報の領域から PID を取得しようとする試みになります。
その結果、いかなる情報も正しく読み取ることができず、呼び出しは失敗の状態として返されなければなりません。

次に、どの項目が含まれているか、そして項目がどのように並んでいるかを知る必要があります。
錠前の最初のスロットには Uptime があり、そのサイズは 2^64 です。
これを 2^32 で読み取ろうとすると、ビット列は完全には読み取られません。
低レベルの工夫を行うのでない限り、このような部分的なバイナリを使用することはできません。

64 ビットのバイナリデータを読み取った後、ようやく二番目のスロットに到達します。
それは、直前の 64 ビット整数を読み取った後にのみ正確に読み取ることができます。
このような厳密で論理的な流れを繰り返すことで、システムから適切な情報を得て、読み取ったデータを適切に扱うことができます。

## 「変数名」を省略する方法

変数そのものを「省略」することはできませんが、使用される変数と破棄される変数を区別することが重要です。
プログラムの用途が十分に明確である場合、たとえ永久に使用されない値であっても、各値にラベルを付けるよりも、名前のない変数をプレースホルダーとして使用するほうが望ましいです。
「空きメモリチェッカー」という例で確認してみましょう。

## 例 - 空きメモリチェッカー

空きメモリやスワップを確認する際には、異なるリソースを示すその他の情報は必要ありません。
より見やすくするために、特定の領域を保持する匿名変数を作成できます。

```go
package main

import (
	"fmt"
	"syscall"
	"unsafe"
)

type sysinfo_t struct {
	_          int64
	_         [3]uint64
	Totalram  uint64
	Freeram   uint64
	Sharedram uint64
	Bufferram uint64
	Totalswap uint64
	Freeswap  uint64
	_         uint16  // 匿名であり、未使用のものは _ で示す
	_         uint16
	_         [4]byte
	_         uint64
	_         uint64
	MemUnit   uint32
	_         [4]byte
}

func main() {
	var info sysinfo_t
	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
	if errno != 0 {
		fmt.Println("sysinfo syscall failed:", errno)
		return
	}

	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
		info.Totalram*uint64(info.MemUnit)/1024/1024,
		info.Freeram*uint64(info.MemUnit)/1024/1024,
		info.Bufferram*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Swap: total=%d MB free=%d MB\n",
		info.Totalswap*uint64(info.MemUnit)/1024/1024,
		info.Freeswap*uint64(info.MemUnit)/1024/1024)
}
```

その結果、変数はラベルなしで読み取られます。
匿名の値も実際には構造体に格納されていますが、コード上にはラベルや判読可能な目印はありません。

## まとめ

- Go の `syscall` と `unsafe` を使用することは、依然として C/CGo を使用するよりも安全です
- 容易に拡張できる大規模なプロジェクトを作成する場合:
  - 匿名変数は使用せず、各メンバーに名前を付けてください。
- 用途が限定的なプロジェクトを作成する場合:
  - 実際には使用されない領域を保持するために匿名変数を使用できます。
- Go の `syscall` は、低レベルの呼び出しを扱うための強力で現代的な手段です

## さらに読む

[syscall](https://pkg.go.dev/syscall)
[unsafe](https://pkg.go.dev/unsafe)
[x/sys/unix](https://pkg.go.dev/golang.org/x/sys/unix)
