---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Direkta systemanrop i Linux i ren Go med syscall.Syscall och unsafe.Pointer
description: Att direkt anropa Linux-kärnans systemanrop, såsom SYS_SYSINFO, från ren Go med ABI-anpassade strukturer, unsafe.Pointer och det standardiserade paketet syscall utan cgo.
language: sv
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Sammanfattning

Vi kommer att lära oss om direkta systemanrop i Go.
Eftersom Go erbjuder strikta kompilatorfel och en stram skräpsamlare (GC) är det mycket bättre att ersätta lågnivåanrop med ren Go.
Lyckligtvis är de flesta av C-funktionsanropen fullständigt omimplementerade i Go, på ett bra och modernt sätt.
Låt oss titta på det.

## Systemanrop

Ett systemanrop är en direkt begäran till operativsystemet.
Eftersom systemet vanligtvis är skrivet i en strikt, föråldrad stil eftersom det körs direkt på hårdvaran, måste vi beakta att dess anrop måste leverera en strikt och korrekt form av begäran.
Så, även om vi inte behöver vissa variabler, måste vi ändå ange storleken oavsett användning.
Låt oss kontrollera detta med ett fullt fungerande exempel.

## Fullständigt exempel

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

Detta exempel innehåller alla variabler och skriver ut omfattande information om aktuell systeminformation.
Vi kan jämföra denna kod med ett lås och en nyckel.
`syscall.SYS_SYSINFO` är en nyckel som låser upp ett lås som finns inuti en kärna.
Därför är det viktigt att använda rätt nyckel för låset.
Vad händer när vi använder `syscall.SYS_GETPID` för detta anrop?
Detta är en nyckel till ett lås som innehåller process-ID:t.
Detta kommer att försöka hämta ett PID från ett utrymme för systeminformation.
Som ett resultat kan ingen av informationen läsas korrekt; anropet måste returnera ett misslyckat tillstånd.

Nu behöver vi veta vilka objekt som finns och hur objekten är ordnade.
I den första facket i låset finns Uptime, med en storlek på 2^64.
Om vi försöker läsa detta med 2^32 läses bitsekvensen inte fullständigt.
Vi kan inte använda sådana partiella binärer om vi inte ska skriva lågnivåtrick.

Efter att ha läst 64 bitar av binärdata befinner vi oss slutligen på ett andra fack.
Det kan endast läsas korrekt när vi har läst föregående 64-bitars heltal.

Genom att upprepa dessa strikta och logiska flöden för att få korrekt information från ett system kan vi hantera den lästa datan på rätt sätt.

## Hur man hoppar över 'variabelnamn'

Även om vi inte kan 'hoppa över' variabler själva är det viktigt att skilja mellan använda och kasserade variabler.
Om programmets användning är tillräckligt tydlig är det bättre att använda namnlösa variabler som platshållare än att märka varje värde, även om de inte används för alltid.
Låt oss kontrollera detta med ett exempel, "Free Memory Checker".

## Exempel: Free Memory Checker

När vi kontrollerar ledigt minne/swap behöver vi inte annan information som indikerar andra resurser.
För att uppnå bättre överskådlighet kan du skapa anonyma variabler för att hålla specifika utrymmen.

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

Följaktligen läses variablerna utan etiketter.
Även om anonyma värden faktiskt lagras i en struktur finns det inga etiketter eller läsbara markeringar i koden.

## Slutsats

- Att använda Gos `syscall` och `unsafe` är fortfarande säkrare än C/CGo
- Om du skriver ett stort projekt som lätt kan utökas:
  - Skapa inte anonyma variabler; ge varje medlem ett namn.
- Om du skriver ett projekt med begränsad användning:
  - Du kan använda anonyma variabler för att hålla utrymmen som faktiskt är oanvända.
- Gos `syscall` är kraftfullt och modernt för att hantera lågnivåanrop

## Läs vidare

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