---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Llamadas directas al sistema Linux en Go puro con syscall.Syscall y unsafe.Pointer
description: Invocación de llamadas al sistema del kernel de Linux, como SYS_SYSINFO, directamente desde Go puro mediante estructuras alineadas con la ABI, unsafe.Pointer y el paquete estándar syscall, sin cgo.
language: es
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Resumen

Aprenderemos sobre las llamadas directas al sistema (system call) en Go.
Dado que Go ofrece errores de compilación estrictos y un recolector de basura (GC) riguroso, es mucho mejor reemplazar las llamadas de bajo nivel por Go puro.
Afortunadamente, la mayoría de las llamadas a funciones de C están completamente reimplementadas en Go, de una manera buena y contemporánea.
Echemos un vistazo.

## Llamada al sistema

Una llamada al sistema (system call) es una solicitud directa al sistema operativo.
Dado que el sistema suele escribirse con un estilo rígido y anticuado, ya que se ejecuta directamente sobre el hardware, debemos tener en cuenta que su llamada debe entregar una forma de solicitud estricta y correcta.
Por lo tanto, aunque no necesitemos algunas variables, aún debemos completar el tamaño independientemente de su uso.
Veamos un ejemplo completamente funcional.

## Ejemplo 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 ejemplo incluye todas las variables e imprime información extensa sobre la información del sistema actual.
Podemos comparar este código con una cerradura y una llave.
`syscall.SYS_SYSINFO` es una llave que abre una cerradura que se encuentra dentro del kernel.
Por lo tanto, es importante usar la llave correcta para la cerradura.
¿Qué ocurrirá si usamos `syscall.SYS_GETPID` para esta llamada?
Esta es una llave para una cerradura que contiene el ID de proceso.
Esto intentará obtener un PID desde un espacio destinado a la información del sistema.
Como resultado, no se puede leer correctamente ninguna de la información; la llamada debe devolver un estado de fallo.

Ahora, necesitamos saber qué elementos contiene y cómo están ordenados.
En la primera ranura de la cerradura tenemos Uptime, con un tamaño de 2^64.
Si intentamos leerlo con 2^32, la secuencia de bits no se lee completamente.
No podemos utilizar este tipo de fragmentos binarios a menos que vayamos a escribir trucos de bajo nivel.

Tras leer 64 bits de datos binarios, finalmente llegamos a la segunda ranura.
Solo puede leerse con precisión cuando hemos leído previamente el entero de 64 bits anterior.

Repitiendo estos flujos estrictos y lógicos para obtener información adecuada del sistema, podemos manejar correctamente los datos leídos.

## Cómo omitir los 'nombres de variables'

Aunque no podemos 'omitir' las variables en sí, es importante distinguir entre las variables utilizadas y las descartadas.
Si el uso del programa es suficientemente claro, es preferible usar variables sin nombre como marcadores de posición en lugar de etiquetar cada valor, aunque no se utilicen de forma permanente.
Veamos esto con un ejemplo, "Verificador de memoria libre".

## Ejemplo: Verificador de memoria libre

Al comprobar la memoria libre y el swap, no necesitamos otra información que indique recursos diferentes.
Para lograr una mejor visibilidad, puede crear variables anónimas para contener espacios 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)
}
```

En consecuencia, las variables se leen sin etiquetas.
Aunque los valores anónimos se almacenan realmente en una estructura, no hay etiquetas ni marcas legibles en el código.

## Conclusión

- Usar `syscall` y `unsafe` de Go sigue siendo más seguro que C/CGo
- Si está escribiendo un proyecto grande que puede expandirse fácilmente:
  - No use variables anónimas; asigne nombres a cada uno de los miembros.
- Si está escribiendo un proyecto con un uso limitado:
  - Puede usar variables anónimas para ocupar espacios que realmente no se utilizan.
- El `syscall` de Go es potente y moderno para manejar llamadas de bajo nivel

## Lectura adicional

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