GoSuda

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

By Yunjin Lee
views ...

Résumé

Nous allons étudier l'appel système direct sur Go. Étant donné que Go offre des erreurs de compilateur strictes et un GC rigide, il est préférable de remplacer les appels de bas niveau en Pure Go. Heureusement, la plupart des appels de fonctions C sont entièrement réimplémentés en Go, de manière adéquate et contemporaine. Examinons cela.

Appel Système

L'appel système est une requête directe au système d'exploitation. Puisque le système est habituellement é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 fournir une forme de requête stricte et correcte. Ainsi, même si nous n'avons pas besoin de certaines variables, nous devons quand même renseigner la taille, quelle que soit l'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	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
29	if errno != 0 {
30		fmt.Println("sysinfo syscall failed:", errno)
31		return
32	}
33
34	scale := float64(1 << 16)
35	fmt.Printf("Uptime: %d seconds\n", info.Uptime)
36	fmt.Printf("Load Average: %.2f %.2f %.2f\n",
37		float64(info.Loads[0])/scale,
38		float64(info.Loads[1])/scale,
39		float64(info.Loads[2])/scale)
40	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
41		info.Totalram*uint64(info.MemUnit)/1024/1024,
42		info.Freeram*uint64(info.MemUnit)/1024/1024,
43		info.Bufferram*uint64(info.MemUnit)/1024/1024)
44	fmt.Printf("Swap: total=%d MB free=%d MB\n",
45		info.Totalswap*uint64(info.MemUnit)/1024/1024,
46		info.Freeswap*uint64(info.MemUnit)/1024/1024)
47	fmt.Printf("Processes: %d\n", info.Procs)
48}

Cet exemple inclut toutes les variables et affiche des informations détaillées sur le système actuel. 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 noyau. Par conséquent, il est important d'utiliser la clé correcte pour un casier. Que se passera-t-il si nous utilisons syscall.SYS_GETPID pour cet appel ? C'est la clé d'un casier qui contient l'ID de Processus. Ceci tentera d'obtenir un PID à partir de l'espace destiné aux informations système. En conséquence, aucune des informations ne peut être lue correctement ; l'appel doit être renvoyé comme étant en état d'échec.

Maintenant, nous devons savoir quels éléments sont contenus et comment les éléments sont ordonnés. Dans le premier emplacement d'un casier, nous avons Uptime, avec une taille de 2^64. Si nous essayons de lire ceci 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 que nous n'ayons l'intention 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 taille 64 bits.

En répétant ces flux stricts et logiques pour obtenir une information appropriée 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 "Vérificateur de Mémoire Libre".

Exemple - Vérificateur de Mémoire Libre

Lors de la vérification de la Mémoire Libre/Swap, 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  // anonymous, and unused ones are marked as _ (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	_, _, 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	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
36		info.Totalram*uint64(info.MemUnit)/1024/1024,
37		info.Freeram*uint64(info.MemUnit)/1024/1024,
38		info.Bufferram*uint64(info.MemUnit)/1024/1024)
39	fmt.Printf("Swap: total=%d MB free=%d MB\n",
40		info.Totalswap*uint64(info.MemUnit)/1024/1024,
41		info.Freeswap*uint64(info.MemUnit)/1024/1024)
42}

Par conséquent, les variables sont lues sans étiquettes. Bien que les valeurs anonymes soient effectivement 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 énorme qui peut être étendu facilement :
    • Ne créez pas de variables anonymes ; donnez des noms à chaque membre.
  • Si vous écrivez un projet dont l'utilisation est limitée :
    • Vous pouvez utiliser des variables anonymes pour contenir des espaces qui sont en réalité inutilisés.
  • syscall de Go est puissant et moderne pour gérer les appels de bas niveau

Pour en savoir plus

syscall unsafe x/sys/unix