La syscall Go è un brillante sostituto dell'I/O di basso livello
Summary
Apprenderemo le chiamate di sistema dirette su Go. Poiché Go offre errori di compilazione rigorosi e un GC rigido, è molto meglio sostituire le chiamate di basso livello in Pure Go. Fortunatamente, la maggior parte delle chiamate di funzione C sono completamente re-implementate in Go, in modo valido e contemporaneo. Osserviamolo.
System Call
La chiamata di sistema (System call) è una richiesta diretta al sistema operativo. Poiché il sistema è solitamente scritto in uno stile rigido e obsoleto, dato che viene eseguito direttamente su un hardware, dobbiamo considerare che la sua chiamata deve fornire una forma di richiesta rigorosa e corretta. Quindi, anche se non abbiamo bisogno di alcune variabili, dobbiamo comunque specificare la dimensione indipendentemente dall'uso. Verifichiamo con un esempio completamente funzionante.
Full Example
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}
Questo esempio include tutte le variabili e stampa informazioni estese sulle informazioni del sistema attuale.
Possiamo paragonare questo codice a un armadietto e una chiave.syscall.SYS_SYSINFO è una chiave che sblocca un armadietto che si trova all'interno di un kernel.
Pertanto, è importante utilizzare la chiave corretta per un armadietto.
Cosa succederà se usiamo syscall.SYS_GETPID per questa chiamata?
Questa è una chiave per un armadietto che contiene l'ID di Processo (Process ID).
Questo tenterà di ottenere un PID da uno spazio destinato alle informazioni di sistema.
Di conseguenza, nessuna delle informazioni può essere letta correttamente; la chiamata deve essere restituita come stato fallito.
Ora, dobbiamo sapere quali elementi sono contenuti e come sono ordinati. Nel primo slot di un armadietto, abbiamo Uptime, con una dimensione di 2^64. Se tentiamo di leggerlo con 2^32, la sequenza di bit non viene letta completamente. Non possiamo usare questo tipo di binari parziali a meno che non si vogliano scrivere trucchi di basso livello.
Dopo aver letto 64 bit di dati binari, finalmente siamo nel secondo slot. Può essere letto accuratamente solo se abbiamo letto l'integer precedente di dimensione 64 bit.
Ripetendo questi flussi rigorosi e logici per ottenere un'informazione appropriata da un sistema, possiamo gestire correttamente i dati letti.
How to skip 'variable names'
Anche se non possiamo 'saltare' le variabili stesse, è importante distinguere le variabili usate da quelle scartate. Se l'uso del programma è sufficientemente chiaro, è meglio usare variabili senza nome come segnaposto piuttosto che etichettare ogni valore anche se non verrà mai utilizzato. Verifichiamo questo con un esempio, "Free Memory Checker" (Verificatore di Memoria Libera)
Example - Free Memory Checker
Quando verifichiamo la Memoria Libera/Swap, non abbiamo bisogno di altre informazioni che indicano risorse diverse. Per ottenere una migliore visibilità, è possibile creare variabili anonime per contenere spazi specifici.
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 _ (quelle anonime e non usate sono contrassegnate come _)
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}
Di conseguenza, le variabili vengono lette senza etichette. Sebbene i valori anonimi siano effettivamente memorizzati in una struttura, non ci sono etichette/segni leggibili nel codice.
Conclusion
- L'uso di
syscalleunsafedi Go è comunque più sicuro di C/CGo - Se si sta scrivendo un progetto enorme che può essere espanso facilmente:
- Non creare variabili anonime; assegna nomi a ciascun membro.
- Se si sta scrivendo un progetto con un uso limitato:
- È possibile utilizzare variabili anonime per contenere spazi che in realtà non vengono utilizzati.
- Il
syscalldi Go è potente e moderno per gestire chiamate di basso livello