---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Chamadas de Sistema Linux Diretas em Go Puro com syscall.Syscall e unsafe.Pointer
description: Invocação de chamadas de sistema do kernel Linux, como SYS_SYSINFO, diretamente a partir de Go puro, utilizando structs alinhadas à ABI, unsafe.Pointer e o pacote syscall padrão, sem cgo.
language: pt
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Resumo

Vamos aprender sobre chamadas de sistema diretas em Go.
Como Go oferece erros de compilação rigorosos e um GC rígido, é muito melhor substituir chamadas de baixo nível por Go puro.
Felizmente, a maioria das chamadas de função em C foi totalmente reimplementada em Go, de forma boa e contemporânea.
Vamos dar uma olhada nisso.

## Chamada de Sistema

Uma chamada de sistema é uma requisição direta ao sistema operacional.
Como o sistema costuma ser escrito em um estilo rígido e antiquado, já que roda diretamente sobre o hardware, precisamos considerar que sua chamada deve entregar uma forma estrita e correta de requisição.
Assim, mesmo que não precisemos de algumas variáveis, ainda precisamos preencher o tamanho, independentemente do uso.
Vamos verificar com um exemplo totalmente funcional.

## Exemplo Completo

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

Este exemplo inclui todas as variáveis e imprime informações extensas sobre as informações atuais do sistema.
Podemos comparar este código com uma fechadura e uma chave.
`syscall.SYS_SYSINFO` é uma chave que abre uma fechadura que está dentro do kernel.
Portanto, usar a chave correta para a fechadura é importante.
O que acontecerá se usarmos `syscall.SYS_GETPID` nesta chamada?
Esta é a chave para uma fechadura que contém o Process ID.
Isso tentará obter um PID a partir de um espaço destinado a informações do sistema.
Como resultado, nenhuma das informações pode ser lida corretamente; a chamada deve retornar em estado de falha.

Agora, precisamos saber quais itens estão contidos e como os itens estão ordenados.
No primeiro slot da fechadura, temos Uptime, com tamanho de 2^64.
Se tentarmos lê-lo com 2^32, a sequência de bits não é lida por completo.
Não podemos usar esses tipos de binários parciais, a menos que pretendamos escrever truques de baixo nível.

Após ler 64 bits de dados binários, finalmente chegamos ao segundo slot.
Ele só pode ser lido com precisão quando tivermos lido o inteiro de 64 bits anterior.

Repetindo esses fluxos rigorosos e lógicos para obter informações adequadas do sistema, podemos tratar corretamente os dados lidos.

## Como pular 'nomes de variáveis'

Embora não possamos 'pular' as próprias variáveis, é importante distinguir variáveis usadas das descartadas.
Se o uso do programa for claro o suficiente, é melhor usar variáveis sem nome como marcadores de posição do que rotular cada valor, mesmo que não sejam usadas para sempre.
Vamos verificar isso com um exemplo, "Verificador de Memória Livre".

## Exemplo - Verificador de Memória Livre

Ao verificar a Memória Livre/Swap, não precisamos de outras informações que indicam diferentes recursos.
Para obter melhor visibilidade, você pode criar variáveis anônimas para conter espaços específicos.

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

Consequentemente, as variáveis são lidas sem rótulos.
Embora os valores anônimos sejam de fato armazenados em uma estrutura, não há rótulos ou marcas legíveis no código.

## Conclusão

- Usar `syscall` e `unsafe` do Go ainda é mais seguro do que C/CGo
- Se você estiver escrevendo um projeto grande que possa ser expandido facilmente:
  - Não crie variáveis anônimas; dê nomes a cada membro.
- Se você estiver escrevendo um projeto de uso limitado:
  - Você pode usar variáveis anônimas para ocupar espaços que realmente não são usados.
- O `syscall` do Go é poderoso e moderno para lidar com chamadas de baixo nível

## Leia Mais

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