Go syscall est un brillant remplacement des I/O de bas niveau
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
syscalletunsafede 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.
syscallde Go est puissant et moderne pour gérer les appels de bas niveau