---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Direkte Linux-systemkall i ren Go med syscall.Syscall og unsafe.Pointer
description: Å kalle Linux-kjernens systemkall, som SYS_SYSINFO, direkte fra ren Go ved hjelp av ABI-justerte strukturer, unsafe.Pointer og den standard syscall-pakken uten cgo.
language: "no"
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Sammendrag

Vi skal lære om direkte systemkall i Go.
Ettersom Go tilbyr strenge kompileringsfeil og en stiv GC, er det langt bedre å erstatte lavnivåkall med ren Go.
Heldigvis er de fleste C-funksjonskallene fullt ut reimplementert i Go, på en god og moderne måte.
La oss se nærmere på dette.

## Systemkall

Et systemkall er en direkte forespørsel til operativsystemet.
Ettersom systemet vanligvis er skrevet i en stiv, gammeldags stil, da det kjører direkte på maskinvaren, må vi ta hensyn til at kallet må utgjøres i en streng og korrekt form.
Derfor må vi, selv om vi ikke trenger enkelte variabler, likevel angi størrelsen, uavhengig av bruk.
La oss se på et fullt fungerende eksempel.

## Fullstendig eksempel

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

Dette eksemplet inkluderer alle variabler og skriver ut omfattende informasjon om gjeldende systeminformasjon.
Vi kan sammenligne denne koden med et lås og en nøkkel.
`syscall.SYS_SYSINFO` er en nøkkel som låser opp et skap som befinner seg inne i kjernen.
Derfor er det viktig å bruke riktig nøkkel til skapet.
Hva skjer når vi bruker `syscall.SYS_GETPID` for dette kallet?
Dette er en nøkkel til et skap som inneholder prosess-ID-en.
Dette vil forsøke å hente en PID fra et område for systeminformasjon.
Som et resultat kan ingen av informasjonene leses korrekt; kallet må returneres med tilstanden mislykket.

Nå må vi vite hvilke elementer som finnes, og hvordan elementene er ordnet.
I den første plassen i skapet finner vi Uptime, med en størrelse på 2^64.
Hvis vi forsøker å lese dette med 2^32, leses ikke bitsekvensen fullt ut.
Vi kan ikke bruke slike delvise binærdata, med mindre vi skal skrive lavnivåtriks.

Etter å ha lest 64 bits binærdata, er vi endelig ved en andre plass.
Den kan bare leses nøyaktig når vi har lest det forrige heltallet på 64 bits.

Ved å gjenta disse strenge og logiske trinnene for å få riktig informasjon fra systemet, kan vi håndtere de leste dataene på en korrekt måte.

## Hvordan hoppe over «variabelnavn»

Selv om vi ikke kan «hoppe over» variabler selv, er det viktig å skille mellom brukte og forkastede variabler.
Hvis programmets formål er tilstrekkelig klart, er det bedre å bruke navnløse variabler som plassholdere enn å merke hver verdi, selv om de ikke brukes permanent.
La oss se på dette med et eksempel, «Free Memory Checker».

## Eksempel - Free Memory Checker

Når vi kontrollerer ledig minne og swap, trenger vi ikke annen informasjon som angir andre ressurser.
For å oppnå bedre oversikt kan du lage anonyme variabler som holder bestemte områder.

```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  // anonyme, og ubrukte, markeres med _
	_         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)
}
```

Følgelig leses variablene uten etiketter.
Selv om de anonyme verdiene faktisk lagres i en struktur, finnes det ingen etiketter eller lesbare merker i koden.

## Konklusjon

- Å bruke Gos `syscall` og `unsafe` er fortsatt tryggere enn C/CGo
- Hvis du skriver et stort prosjekt som enkelt kan utvides:
  - Ikke bruk anonyme variabler; gi hvert medlem et eget navn.
- Hvis du skriver et prosjekt med begrenset bruksområde:
  - Du kan bruke anonyme variabler til å holde plasser som faktisk ikke brukes.
- Gos `syscall` er kraftig og moderne når det gjelder håndtering av lavnivåkall

## Les videre

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