---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Directe Linux-systeemaanroepen in puur Go met syscall.Syscall en unsafe.Pointer
description: Het rechtstreeks aanroepen van Linux-kernelsystemaanroepen zoals SYS_SYSINFO vanuit puur Go, met behulp van ABI-uitgelijnde structs, unsafe.Pointer en het standaardpakket syscall zonder cgo.
language: nl
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Samenvatting

We zullen leren over directe systeemaanroepen in Go.
Aangezien Go strikte compilerfouten en een rigide GC biedt, is het veel beter om laag-niveau aanroepen te vervangen door Pure Go.
Gelukkig zijn de meeste C-functieaanroepen volledig opnieuw geïmplementeerd in Go, op een goede en hedendaagse manier.
Laten we er eens naar kijken.

## Systeemaanroep

Een systeemaanroep is een directe aanvraag aan het besturingssysteem.
Aangezien het systeem doorgaans is geschreven in een rigide, ouderwetse stijl omdat het rechtstreeks op de hardware draait, moeten we er rekening mee houden dat de aanroep een strikte en correcte vorm van aanvraag moet aanleveren.
Dus, zelfs als we sommige variabelen niet nodig hebben, moeten we toch de grootte invullen, ongeacht het gebruik.
Laten we dit controleren met een volledig werkend voorbeeld.

## Volledig voorbeeld

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

Dit voorbeeld bevat alle variabelen en drukt uitgebreide informatie over de huidige systeeminformatie af.
We kunnen deze code vergelijken met een slot en een sleutel.
`syscall.SYS_SYSINFO` is een sleutel die een slot ontgrendelt dat zich in een kernel bevindt.
Daarom is het belangrijk om de juiste sleutel voor het slot te gebruiken.
Wat gebeurt er als we `syscall.SYS_GETPID` voor deze aanroep gebruiken?
Dit is een sleutel voor een slot dat een proces-ID bevat.
Dit zal proberen een PID op te halen uit een ruimte voor systeeminformatie.
Als gevolg hiervan kan geen enkele informatie correct worden gelezen; de aanroep moet als mislukt worden teruggegeven.

Nu moeten we weten welke onderdelen er zijn opgenomen en hoe de onderdelen zijn geordend.
In de eerste sleuf van een slot hebben we Uptime, met een grootte van 2^64.
Als we dit proberen te lezen met 2^32, wordt de bitreeks niet volledig gelezen.
We kunnen dergelijke gedeeltelijke binaire gegevens niet gebruiken, tenzij we laag-niveau trucs gaan schrijven.

Na het lezen van 64 bits aan binaire gegevens komen we eindelijk bij een tweede sleuf.
Deze kan alleen nauwkeurig worden gelezen wanneer we het vorige 64-bits gehele getal hebben gelezen.

Door deze strikte en logische stappen herhaaldelijk uit te voeren, om de juiste informatie uit een systeem te verkrijgen, kunnen we de gelezen gegevens correct verwerken.

## Hoe 'variabelenamen' over te slaan

Hoewel we variabelen zelf niet kunnen 'overslaan', is het belangrijk om gebruikte variabelen van weggegooide variabelen te onderscheiden.
Als het doel van het programma duidelijk genoeg is, is het beter om naamloze variabelen als plaatshouders te gebruiken dan elke waarde te labelen, zelfs als ze niet voor altijd worden gebruikt.
Laten we dit controleren met een voorbeeld, "Free Memory Checker"

## Voorbeeld - Free Memory Checker

Bij het controleren van vrij geheugen/swap hebben we geen andere informatie nodig die op verschillende bronnen wijst.
Om een betere overzichtelijkheid te bereiken, kun je anonieme variabelen maken om specifieke ruimtes vast te houden.

```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  // anoniem, en ongebruikte velden worden gemarkeerd als _
	_         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)
}
```

Hierdoor worden variabelen zonder labels gelezen.
Hoewel anonieme waarden daadwerkelijk in een structuur zijn opgeslagen, zijn er geen labels of leesbare markeringen in de code.

## Conclusie

- Het gebruik van Go's `syscall` en `unsafe` is nog steeds veiliger dan C/CGo
- Als je een groot project schrijft dat eenvoudig uitgebreid kan worden:
  - Maak geen anonieme variabelen; geef elk lid een naam.
- Als je een project schrijft met beperkt gebruik:
  - Je kunt anonieme variabelen gebruiken om ruimtes vast te houden die daadwerkelijk ongebruikt zijn.
- Go's `syscall` is krachtig en modern om laag-niveau aanroepen af te handelen

## Verder lezen

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