---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Appels système Linux directs en Go pur avec syscall.Syscall et unsafe.Pointer
description: Invocation directe des appels système du noyau Linux, tels que SYS_SYSINFO, depuis du Go pur à l'aide de structures alignées sur l'ABI, de unsafe.Pointer et du paquet standard syscall, sans cgo.
language: fr
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Résumé

Nous allons apprendre à utiliser les appels système directs en Go.
Étant donné que Go offre des erreurs de compilation strictes et un ramasse-miettes (GC) rigide, il est bien préférable de remplacer les appels de bas niveau par du Pure Go.
Heureusement, la plupart des appels de fonctions C sont entièrement réimplémentés en Go, de manière élégante et moderne.
Examinons cela.

## Appel système

Un appel système est une requête directe adressée au système d'exploitation.
Le système étant généralement écrit dans un style rigide et ancien, puisqu'il fonctionne directement sur le matériel, nous devons veiller à ce que son appel transmette une forme de requête stricte et correcte.
Ainsi, même si nous n'avons pas besoin de certaines variables, nous devons tout de même en spécifier la taille, indépendamment de leur utilisation.
Voyons un exemple entièrement fonctionnel.

## Exemple complet

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

Cet exemple inclut toutes les variables et affiche de nombreuses informations détaillées sur les informations système actuelles.
On peut comparer ce code à une serrure et à une clé.
`syscall.SYS_SYSINFO` est une clé qui ouvre une serrure située à l'intérieur du noyau.
Par conséquent, il est important d'utiliser la bonne clé pour la serrure.
Que se passe-t-il si nous utilisons `syscall.SYS_GETPID` pour cet appel ?
Il s'agit d'une clé pour une serrure contenant l'identifiant de processus (PID).
Cela tentera d'obtenir un PID à partir d'un espace réservé aux informations système.
En conséquence, aucune information ne peut être lue correctement ; l'appel doit donc être signalé comme ayant échoué.

Maintenant, nous devons savoir quels éléments sont contenus et comment ils sont ordonnés.
Dans le premier emplacement de la serrure, nous trouvons Uptime, d'une taille de 2^64.
Si nous tentons de le lire avec 2^32, la séquence de bits n'est pas entièrement lue.
Nous ne pouvons pas utiliser ce type de fragments binaires, sauf si nous sommes prêts à recourir à des astuces de bas niveau.

Après la lecture de 64 bits de données binaires, nous atteignons enfin le deuxième emplacement.
Il ne peut être lu correctement qu'après lecture de l'entier de 64 bits précédent.

En répétant ces flux stricts et logiques, nous pouvons obtenir les informations appropriées du système et traiter correctement les données lues.

## Comment omettre les « noms de variables »

Bien qu'il ne soit pas possible de « omettre » les variables elles-mêmes, il est important de distinguer les variables utilisées de celles qui sont écartées.
Si l'usage du programme est suffisamment clair, il est préférable d'utiliser des variables sans nom comme espaces réservés plutôt que d'étiqueter chaque valeur, même si elles ne sont pas utilisées de façon permanente.
Vérifions cela avec un exemple, le « Vérificateur de mémoire libre ».

## Exemple : Vérificateur de mémoire libre

Lors de la vérification de la mémoire libre et de l'espace d'échange (swap), nous n'avons pas besoin des autres informations qui indiquent d'autres ressources.
Pour une meilleure lisibilité, vous pouvez créer des variables anonymes pour réserver des emplacements spécifiques.

```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  // anonyme, et les éléments inutilisés sont marqués _
	_         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)
}
```

Par conséquent, les variables sont lues sans étiquettes.
Bien que les valeurs anonymes soient effectivement stockées dans une structure, aucune étiquette ni marque lisible n'apparaît dans le code.

## Conclusion

- Utiliser `syscall` et `unsafe` de Go reste plus sûr que C/CGo
- Si vous écrivez un projet important et facilement extensible :
  - Ne créez pas de variables anonymes ; donnez un nom à chaque membre.
- Si vous écrivez un projet à usage limité :
  - Vous pouvez utiliser des variables anonymes pour réserver les emplacements réellement inutilisés.
- Le package `syscall` de Go est puissant et moderne pour gérer les appels de bas niveau

## Pour aller plus loin

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