---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Direkte Linux-Systemaufrufe in reinem Go mit syscall.Syscall und unsafe.Pointer
description: Der direkte Aufruf von Linux-Kernel-Systemaufrufen wie SYS_SYSINFO aus reinem Go heraus, unter Verwendung von ABI-konformen Structs, unsafe.Pointer und des Standardpakets syscall ohne cgo.
language: de
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Zusammenfassung

Wir werden etwas über direkte Systemaufrufe in Go lernen.
Da Go strikte Compiler-Fehlermeldungen und einen rigiden GC bietet, ist es deutlich besser, low-level Aufrufe durch reines Go zu ersetzen.
Glücklicherweise sind die meisten C-Funktionsaufrufe vollständig in Go neu implementiert, und zwar auf eine gute und zeitgemäße Weise.
Schauen wir uns das an.

## Systemaufruf

Ein Systemaufruf ist eine direkte Anfrage an das Betriebssystem.
Da das System üblicherweise in einem strengen, altmodischen Stil geschrieben ist, weil es direkt auf der Hardware läuft, müssen wir berücksichtigen, dass sein Aufruf eine strikte und korrekte Form einer Anfrage liefern muss.
Selbst wenn wir manche Variablen nicht benötigen, müssen wir deren Größe daher unabhängig von ihrer Verwendung ausfüllen.
Sehen wir uns ein voll funktionsfähiges Beispiel an.

## Vollständiges Beispiel

```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)
}
```

Dieses Beispiel enthält alle Variablen und gibt umfangreiche Informationen über die aktuellen Systeminformationen aus.
Wir können diesen Code mit einem Schloss und einem Schlüssel vergleichen.
`syscall.SYS_SYSINFO` ist ein Schlüssel, der ein Schloss öffnet, das sich im Kernel befindet.
Daher ist es wichtig, den richtigen Schlüssel für ein Schloss zu verwenden.
Was passiert, wenn wir `syscall.SYS_GETPID` für diesen Aufruf verwenden?
Dies ist ein Schlüssel für ein Schloss, das eine Prozess-ID enthält.
Dies versucht, eine PID aus einem Speicherbereich für Systeminformationen abzurufen.
Infolgedessen können keine der Informationen korrekt gelesen werden; der Aufruf muss als fehlgeschlagen zurückgegeben werden.

Nun müssen wir wissen, welche Elemente enthalten sind und wie die Elemente angeordnet sind.
Im ersten Fach eines Schlosses befindet sich Uptime mit einer Größe von 2^64.
Wenn wir dies mit 2^32 lesen würden, wird die Bitfolge nicht vollständig gelesen.
Wir können solche partiellen Binärdaten nicht verwenden, es sei denn, wir schreiben low-level Tricks.

Nach dem Lesen von 64 Bit Binärdaten befinden wir uns schließlich am zweiten Fach.
Es kann nur dann korrekt gelesen werden, wenn wir zuvor die vorherige 64-Bit-Ganzzahl gelesen haben.

Durch die Wiederholung dieser strikten und logischen Abläufe erhalten wir die richtigen Informationen aus dem System, und wir können die gelesenen Daten ordnungsgemäß verarbeiten.

## Wie man 'Variablennamen' überspringt

Auch wenn wir Variablen selbst nicht 'überspringen' können, ist es wichtig, verwendete und verworfene Variablen zu unterscheiden.
Wenn der Zweck des Programms klar genug ist, ist es besser, namenlose Variablen als Platzhalter zu verwenden, anstatt jeden Wert zu benennen, auch wenn sie nicht dauerhaft verwendet werden.
Sehen wir uns das an einem Beispiel an, dem "Free Memory Checker".

## Beispiel - Free Memory Checker

Wenn wir freien Speicher bzw. Swap prüfen, benötigen wir keine anderen Informationen, die auf andere Ressourcen hinweisen.
Um eine bessere Übersichtlichkeit zu erreichen, können Sie anonyme Variablen verwenden, um bestimmte Bereiche zu halten.

```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  // anonymous, and unused ones are marked as _
	_         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)
}
```

Folglich werden Variablen ohne Bezeichnungen gelesen.
Obwohl anonyme Werte tatsächlich in einer Struktur gespeichert werden, gibt es im Code keine Bezeichnungen bzw. lesbaren Markierungen.

## Fazit

- Die Verwendung von Gos `syscall` und `unsafe` ist weiterhin sicherer als C/CGo
- Wenn Sie ein großes Projekt schreiben, das leicht erweitert werden kann:
  - Verwenden Sie keine anonymen Variablen; geben Sie jedem Mitglied einen eigenen Namen.
- Wenn Sie ein Projekt mit begrenztem Einsatzzweck schreiben:
  - Sie können anonyme Variablen verwenden, um Bereiche zu halten, die tatsächlich ungenutzt sind.
- Gos `syscall` ist leistungsstark und modern für die Handhabung von low-level Aufrufen

## Weiterführende Literatur

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