GoSuda

Go syscall est un remplacement brillant des I/O de bas niveau

By Yunjin Lee
views ...

Résumé

Nous allons étudier les system calls directs sur Go. Puisque Go offre des erreurs de compilateur strictes et un GC rigide, il est préférable de remplacer les appels de bas niveau par du Pure Go. Heureusement, la plupart des appels de fonction C sont entièrement réimplémentés en Go, de manière efficace et contemporaine. Examinons cela.

System Call

Un system call est une requête directe au système d'exploitation. Puisque le système est généralement écrit dans un style rigide et désuet, car il s'exécute directement sur le matériel, nous devons considérer que son appel doit délivrer une forme de requête stricte et correcte. Ainsi, même si certaines variables ne sont pas nécessaires, il faut tout de même renseigner la taille, quelle que soit son utilisation. Vérifions cela avec un exemple entièrement fonctionnel.

Exemple complet

 1package main
 2import (
 3	"fmt"
 4	"syscall"
 5	"unsafe"
 6)
 7
 8type sysinfo_t struct {
 9	Uptime    int64
10	Loads     [3]uint64
11	Totalram  uint64
12	Freeram   uint64
13	Sharedram uint64
14	Bufferram uint64
15	Totalswap uint64
16	Freeswap  uint64
17	Procs     uint16
18	Pad       uint16
19	_         [4]byte
20	Totalhigh uint64
21	Freehigh  uint64
22	MemUnit   uint32
23	_         [4]byte
24}
25
26func main() {
27	var info sysinfo_t
28	// Appel du system call SYS_SYSINFO
29	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
30	if errno != 0 {
31		fmt.Println("sysinfo syscall failed:", errno)
32		return
33	}
34
35	scale := float64(1 << 16)
36	fmt.Printf("Uptime: %d seconds\n", info.Uptime)
37	fmt.Printf("Load Average: %.2f %.2f %.2f\n",
38		float64(info.Loads[0])/scale,
39		float64(info.Loads[1])/scale,
40		float64(info.Loads[2])/scale)
41	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
42		info.Totalram*uint64(info.MemUnit)/1024/1024,
43		info.Freeram*uint64(info.MemUnit)/1024/1024,
44		info.Bufferram*uint64(info.MemUnit)/1024/1024)
45	fmt.Printf("Swap: total=%d MB free=%d MB\n",
46		info.Totalswap*uint64(info.MemUnit)/1024/1024,
47		info.Freeswap*uint64(info.MemUnit)/1024/1024)
48	fmt.Printf("Processes: %d\n", info.Procs)
49}

Cet exemple inclut toutes les variables et affiche des informations détaillées sur l'état actuel du système. Nous pouvons comparer ce code à un casier et une clé.syscall.SYS_SYSINFO est une clé qui déverrouille un casier situé à l'intérieur d'un kernel. Par conséquent, l'utilisation de la bonne clé pour un casier est importante. Que se passera-t-il si nous utilisons syscall.SYS_GETPID pour cet appel ? C'est une clé pour un casier qui contient l'ID du Processus. Cela tentera d'obtenir un PID à partir d'un espace dédié aux informations système. En conséquence, aucune des informations ne peut être lue correctement ; l'appel doit être renvoyé comme un état d'échec.

Maintenant, nous devons savoir quels éléments sont contenus et comment ils sont ordonnés. Dans le premier emplacement d'un casier, nous avons l'Uptime, d'une taille de 2^64. Si nous essayons de le lire avec 2^32, la séquence de bits n'est pas entièrement lue. Nous ne pouvons pas utiliser ce genre de binaires partiels à moins d'écrire des astuces de bas niveau.

Après avoir lu 64 bits de données binaires, nous sommes enfin sur le deuxième emplacement. Il ne peut être lu avec précision que si nous avons lu l'entier précédent de 64 bits.

En répétant ces flux stricts et logiques pour obtenir des informations appropriées d'un système, nous pouvons gérer correctement les données lues.

Comment ignorer les « noms de variables »

Même si nous ne pouvons pas « ignorer » les variables elles-mêmes, il est important de distinguer les variables utilisées de celles qui sont écartées. Si l'utilisation du programme est suffisamment claire, 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 jamais utilisées. Vérifions cela avec un exemple, le "Free Memory Checker".

Exemple - Vérificateur de mémoire libre

Lors de la vérification de la mémoire libre/des swaps, nous n'avons pas besoin d'autres informations indiquant des ressources différentes. Pour obtenir une meilleure visibilité, vous pouvez créer des variables anonymes pour contenir des espaces spécifiques.

 1package main
 2
 3import (
 4	"fmt"
 5	"syscall"
 6	"unsafe"
 7)
 8
 9type sysinfo_t struct {
10	_          int64
11	_         [3]uint64
12	Totalram  uint64
13	Freeram   uint64
14	Sharedram uint64
15	Bufferram uint64
16	Totalswap uint64
17	Freeswap  uint64
18	_         uint16  // les variables anonymes et inutilisées sont marquées par _
19	_         uint16  
20	_         [4]byte 
21	_         uint64  
22	_         uint64  
23	MemUnit   uint32
24	_         [4]byte
25}
26
27func main() {
28	var info sysinfo_t
29	// Appel du system call SYS_SYSINFO
30	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
31	if errno != 0 {
32		fmt.Println("sysinfo syscall failed:", errno)
33		return
34	}
35
36	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
37		info.Totalram*uint64(info.MemUnit)/1024/1024,
38		info.Freeram*uint64(info.MemUnit)/1024/1024,
39		info.Bufferram*uint64(info.MemUnit)/1024/1024)
40	fmt.Printf("Swap: total=%d MB free=%d MB\n",
41		info.Totalswap*uint64(info.MemUnit)/1024/1024,
42		info.Freeswap*uint64(info.MemUnit)/1024/1024)
43}

Par conséquent, les variables sont lues sans étiquettes. Bien que les valeurs anonymes soient réellement stockées dans une structure, il n'y a pas d'étiquettes/marques lisibles dans le code.

Conclusion

  • L'utilisation de syscall et unsafe de Go est toujours plus sûre que C/CGo.
  • Si vous écrivez un projet de grande envergure qui peut être facilement étendu :
    • 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 contenir les espaces qui sont effectivement inutilisés.
  • Le syscall de Go est puissant et moderne pour gérer les appels de bas niveau.

Lectures complémentaires

syscall unsafe x/sys/unix