---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Suorat Linux-järjestelmäkutsut puhtaassa Go-kielessä syscall.Syscall- ja unsafe.Pointer-funktioiden avulla
description: Linux-ytimen järjestelmäkutsujen, kuten SYS_SYSINFO, kutsuminen suoraan puhtaasta Go-koodista käyttäen ABI-linjattuja rakenteita, unsafe.Pointer-tyyppiä ja vakiomuotoista syscall-pakettia ilman cgo-ratkaisua.
language: fi
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Yhteenveto

Opimme suoran järjestelmäkutsun käytön Go-kielessä.
Koska Go tarjoaa tiukat kääntäjävirheet ja jäykän roskienkeruun (GC), on paljon parempi korvata matalan tason kutsut puhtaalla Go-koodilla.
Onneksi suurin osa C-funktiokutsuista on toteutettu uudelleen kokonaan Go-kielellä, hyvällä ja nykyaikaisella tavalla.
Tutustutaan siihen.

## Järjestelmäkutsu

Järjestelmäkutsu on suora pyyntö käyttöjärjestelmälle.
Koska järjestelmä on yleensä kirjoitettu jäykällä, vanhanaikaisella tyylillä, sillä se toimii suoraan laitteistolla, meidän on otettava huomioon, että sen kutsun on toimitettava tiukasti ja oikein muotoiltu pyyntö.
Siksi, vaikka emme tarvitsisi joitakin muuttujia, meidän on silti täytettävä niiden koko käytöstä riippumatta.
Tarkastellaan täysin toimivaa esimerkkiä.

## Täysi esimerkki

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

Tämä esimerkki sisältää kaikki muuttujat ja tulostaa kattavasti nykyisen järjestelmän tiedot.
Voimme verrata tätä koodia lukkoon ja avaimeen.
`syscall.SYS_SYSINFO` on avain, joka avaa lukon, joka sijaitsee ytimen sisällä.
Siksi oikean avaimen käyttäminen lukolle on tärkeää.
Mitä tapahtuu, kun käytämme tähän kutsuun `syscall.SYS_GETPID`-vakiota?
Tämä on avain lukolle, joka sisältää prosessitunnuksen.
Tämä yrittää hakea PID:n järjestelmätietojen tilasta.
Tämän seurauksena mitään tietoa ei voida lukea oikein, ja kutsun on palautettava epäonnistunut tila.

Nyt meidän on tiedettävä, mitkä kentät lokerossa ovat ja missä järjestyksessä ne ovat.
Lokeron ensimmäisessä paikassa on Uptime, jonka koko on 2^64.
Jos yritämme lukea tämän 2^32:n kokoisena, bittijono ei lue kokonaan.
Emme voi käyttää tällaisia osittaisia binäärejä, ellemme aio kirjoittaa matalan tason temppuja.

Kun 64 bittiä binääridataa on luettu, olemme vihdoin toisessa paikassa.
Sen voi lukea oikein vain, kun edellinen 64-bittinen kokonaisluku on luettu.

Toistamalla nämä tiukat ja loogiset vaiheet saamme järjestelmästä oikean tiedon, ja voimme käsitellä luettua dataa asianmukaisesti.

## Kuinka ohittaa "muuttujanimet"

Vaikka itse muuttujia ei voi "ohittaa", on tärkeää erottaa käytetyt ja hylätyt muuttujat toisistaan.
Jos ohjelman käyttötarkoitus on riittävän selkeä, on parempi käyttää nimettömiä muuttujia paikanvaraajina kuin nimetä jokainen arvo, vaikka niitä ei lopulta käytettäisi.
Tarkastellaan tätä esimerkillä "Vapaan muistin tarkistin".

## Esimerkki - Vapaan muistin tarkistin

Kun tarkistamme vapaata muistia tai vaihtotilaa, emme tarvitse muita tietoja, jotka viittaavat muihin resursseihin.
Paremman selkeyden saavuttamiseksi voit luoda nimettömiä muuttujia pitämään tietyt tilat.

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

Näin ollen muuttujat luetaan ilman nimiä.
Vaikka nimettömät arvot tallennetaan todellisuudessa rakenteeseen, koodissa ei ole nimiä tai luettavia merkintöjä.

## Päätelmät

- Go-kielen `syscall`- ja `unsafe`-paketin käyttö on edelleen turvallisempaa kuin C/CGo
- Jos kirjoitat laajaa projektia, joka on helppo laajentaa:
  - Älä käytä nimettömiä muuttujia; anna jokaiselle jäsenelle nimi.
- Jos kirjoitat projektia, jolla on rajallinen käyttö:
  - Voit käyttää nimettömiä muuttujia pitämään tilaa tyhjille kentille, joita ei todellisuudessa käytetä.
- Go-kielen `syscall` on tehokas ja moderni työkalu matalan tason kutsujen käsittelyyn

## Lue lisää

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