---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Priame systémové volania Linuxu v čistom Go pomocou syscall.Syscall a unsafe.Pointer
description: Vyvolanie systémových volaní Linux kernelu, ako je SYS_SYSINFO, priamo z čistého Go pomocou štruktúr zarovnaných podľa ABI, unsafe.Pointer a štandardného balíka syscall bez cgo.
language: sk
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Zhrnutie

Naučíme sa o priamom volaní systému v jazyku Go.
Keďže Go ponúka prísne kontroly kompilátora a neústupný GC, je oveľa lepšie nahradiť nízkoúrovňové volania čistým Go.
Našťastie väčšina volaní funkcií jazyka C je plne znovuimplementovaná v Go, dobrým a moderným spôsobom.
Pozrime sa na to.

## Volanie systému

Volanie systému je priamy požiadavok na operačný systém.
Keďže systém je zvyčajne napísaný v neohybnom, staromódnom štýle, pretože beží priamo na hardvéri, musíme zohľadniť, že jeho volanie musí odovzdať prísny a správny tvar požiadavky.
Preto, aj keď niektoré premenné nepotrebujeme, stále musíme vyplniť ich veľkosť bez ohľadu na použitie.
Overme si to na plne funkčnom príklade.

## Úplný prí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 príklad obsahuje všetky premenné a vypisuje podrobné informácie o aktuálnych systémových informáciách.
Tento kód môžeme prirovnať k zámku a kľúču.
`syscall.SYS_SYSINFO` je kľúč, ktorý odomyká zámok nachádzajúci sa vnútri jadra.
Preto je dôležité použiť správny kľúč pre zámok.
Čo sa stane, keď pre toto volanie použijeme `syscall.SYS_GETPID`?
Toto je kľúč k zámku, ktorý obsahuje identifikátor procesu (PID).
Týmto sa pokúsime získať PID z priestoru pre systémové informácie.
Výsledkom je, že žiadnu z informácií nie je možné správne prečítať; volanie musí skončiť so stavom zlyhania.

Teraz musíme vedieť, aké položky sú obsiahnuté a ako sú usporiadané.
V prvom slote zámku sa nachádza Uptime s veľkosťou 2^64.
Ak sa pokúsime čítať to s 2^32, bitová postupnosť nebude prečítaná úplne.
Nemôžeme používať takéto čiastočné binárne údaje, pokiaľ nechceme písať nízkoúrovňové triky.

Po prečítaní 64 bitov binárnych údajov sa konečne dostaneme k druhému slotu.
Môže byť prečítaný presne len vtedy, keď sme prečítali predchádzajúce 64-bitové celé číslo.

Opakovaním týchto prísnych a logických postupov získame správne informácie zo systému a môžeme s prečítanými údajmi správne pracovať.

## Ako preskočiť 'názvy premenných'

Hoci nemôžeme premenné samotné 'preskočiť', je dôležité rozlišovať použité a zahodené premenné.
Ak je účel programu dostatočne jasný, je lepšie použiť bezmenné premenné ako zástupné miesta, než označovať každú hodnotu, aj keď sa navždy nepoužijú.
Overme si to na príklade „Kontrola voľnej pamäte“.

## Príklad – Kontrola voľnej pamäte

Pri kontrole voľnej pamäte/swapu nepotrebujeme ostatné informácie, ktoré označujú iné zdroje.
Pre lepšiu prehľadnosť môžete vytvoriť anonymné premenné, ktoré zachytia konkrétne miesta.

```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ýsledkom je, že premenné sa čítajú bez označení.
Hoci sú anonymné hodnoty v skutočnosti uložené v štruktúre, v kóde nie sú žiadne názvy ani čitateľné označenia.

## Záver

- Používanie `syscall` a `unsafe` v Go je stále bezpečnejšie ako C/CGo
- Ak píšete rozsiahly projekt, ktorý sa dá ľahko rozširovať:
  - Nepoužívajte anonymné premenné; vytvárajte názvy pre každý člen.
- Ak píšete projekt s obmedzeným použitím:
  - Môžete použiť anonymné premenné na zachytenie miest, ktoré sú skutočne nepoužité.
- `syscall` v Go je výkonný a moderný nástroj na prácu s nízkoúrovňovými volaniami

## Ďalšie čítanie

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