---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Přímá volání systému Linux v čistém Go pomocí syscall.Syscall a unsafe.Pointer
description: Vyvolávání systémových volání jádra Linuxu, jako je SYS_SYSINFO, přímo z čistého Go pomocí struktur zarovnaných podle ABI, unsafe.Pointer a standardního balíčku syscall bez cgo.
language: cs
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Shrnutí

Naučíme se o přímých systémových voláních v Go.
Jelikož Go nabízí striktní kontrolu při kompilaci a přísný garbage collector (GC), je mnohem lepší nahradit nízkoúrovňová volání čistým Go.
Naštěstí je většina volání funkcí z C plně reimplementována v Go, a to dobrým a moderním způsobem.
Podívejme se na to.

## Systémové volání

Systémové volání je přímý požadavek na operační systém.
Protože systém je obvykle napsán v přísném, staromódním stylu, neboť běží přímo na hardwaru, musíme počítat s tím, že jeho volání musí být předáno v přesné a správné podobě požadavku.
Proto, i když některé proměnné nepotřebujeme, musíme přesto vyplnit jejich velikost bez ohledu na to, zda je použijeme.
Podívejme se na plně funkční příklad.

## Úplný příklad

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

Tento příklad obsahuje všechny proměnné a vypisuje podrobné informace o aktuálním stavu systému.
Tento kód můžeme přirovnat k zámku a klíči.
`syscall.SYS_SYSINFO` je klíč, který odemyká zámek uvnitř jádra.
Proto je důležité použít správný klíč pro daný zámek.
Co se stane, když pro toto volání použijeme `syscall.SYS_GETPID`?
Jde o klíč k zámku, který obsahuje ID procesu.
Tím se pokusíme získat PID z prostoru určeného pro systémové informace.
Výsledkem je, že žádnou z informací nelze přečíst správně; volání musí skončit ve stavu selhání.

Nyní potřebujeme vědět, jaké položky jsou obsaženy a v jakém pořadí jsou uspořádány.
V prvním slotu zámku je Uptime o velikosti 2^64.
Pokud se pokusíme přečíst tuto hodnotu pomocí 2^32, bitová sekvence nebude přečtena celá.
Nemůžeme používat takovéto částečné binární hodnoty, pokud nechceme psát nízkoúrovňové triky.

Po přečtení 64 bitů binárních dat se konečně dostaneme ke druhému slotu.
Lze ho přečíst správně až tehdy, když jsme přečetli předchozí 64bitové celé číslo.

Opakováním těchto přísných a logických kroků získáme správné informace o systému a můžeme s přečtenými daty správně pracovat.

## Jak přeskočit „názvy proměnných“

I když proměnné samotné nelze „přeskočit“, je důležité rozlišovat mezi použitými a zahozenými proměnnými.
Pokud je účel programu dostatečně jasný, je lepší použít anonymní proměnné jako zástupné symboly, než označovat každou hodnotu, i když nebudou nikdy použity.
Podívejme se na to na příkladu „Kontrola volné paměti“.

## Příklad: Kontrola volné paměti

Při kontrole volné paměti a swapu nepotřebujeme ostatní informace, které označují jiné zdroje.
Pro lepší přehlednost můžete vytvořit anonymní proměnné, které zastupují konkrétní prostory.

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

Výsledkem je, že hodnoty se čtou bez popisků.
Ačkoli se anonymní hodnoty ve skutečnosti ukládají do struktury, v kódu nejsou žádné popisky ani čitelné označení.

## Závěr

- Použití `syscall` a `unsafe` v Go je stále bezpečnější než C/CGo
- Pokud píšete rozsáhlý projekt, který lze snadno rozšiřovat:
  - Nepoužívejte anonymní proměnné; pojmenujte každý člen.
- Pokud píšete projekt s omezeným použitím:
  - Můžete použít anonymní proměnné k zastoupení prostorů, které jsou skutečně nepoužité.
- `syscall` v Go je výkonný a moderní nástroj pro práci s nízkoúrovňovými voláními

## Další čtení

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