Go syscall ist ein brillanter Ersatz für Low-Level I/O
Zusammenfassung
Wir werden uns mit direkten Systemaufrufen in Go befassen. Da Go strenge Compiler-Fehler und eine rigide GC bietet, ist es wesentlich besser, Low-Level-Aufrufe in reinem Go zu ersetzen. Glücklicherweise sind die meisten C-Funktionsaufrufe in Go auf gute und zeitgemäße Weise vollständig neu implementiert. Werfen wir einen Blick darauf.
Systemaufruf
Ein Systemaufruf ist eine direkte Anforderung an das Betriebssystem. Da das System üblicherweise in einem rigiden, altmodischen Stil geschrieben ist, da es direkt auf der Hardware läuft, müssen wir berücksichtigen, dass sein Aufruf eine strikte und korrekte Form einer Anforderung liefern muss. Selbst wenn wir also einige Variablen nicht benötigen, müssen wir dennoch die Größe unabhängig von der Verwendung angeben. Überprüfen wir dies anhand eines vollständig funktionierenden Beispiels.
Vollständiges Beispiel
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 // Aufruf des Systemaufrufs 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}
Dieses Beispiel umfasst alle Variablen und gibt umfassende Informationen über die aktuellen Systeminformationen aus.
Wir können diesen Code mit einem Schließfach und einem Schlüssel vergleichen.syscall.SYS_SYSINFO ist ein Schlüssel, der ein Schließfach im Kernel aufschließt.
Daher ist die Verwendung des korrekten Schlüssels für ein Schließfach wichtig.
Was passiert, wenn wir syscall.SYS_GETPID für diesen Aufruf verwenden?
Dies ist ein Schlüssel für ein Schließfach, das die Process ID enthält.
Dies würde versuchen, eine PID aus einem Bereich für Systeminformationen zu erhalten.
Infolgedessen können die Informationen nicht korrekt gelesen werden; der Aufruf muss als fehlgeschlagen zurückgegeben werden.
Nun müssen wir wissen, welche Elemente enthalten sind und wie die Elemente angeordnet sind. Im ersten Fach eines Schließfachs haben wir Uptime mit einer Größe von c90263519492728e7cc2d0ce840057b6 Wenn wir versuchen, dies mit c90263519492728e7cc2d0ce840057b6 Wir können diese Art von partiellen Binärdaten nicht verwenden, es sei denn, wir beabsichtigen, Low-Level-Tricks zu schreiben.
Nach dem Lesen von 64 Bit Binärdaten befinden wir uns schließlich im zweiten Fach. Dies kann nur dann präzise gelesen werden, wenn wir die vorherige 64-Bit-Ganzzahl gelesen haben.
Durch die Wiederholung dieser strikten und logischen Abläufe, um korrekte Informationen von einem System zu erhalten, können wir die gelesenen Daten ordnungsgemäß verarbeiten.
Wie man 'Variablennamen' überspringt
Obwohl wir Variablen selbst nicht 'überspringen' können, ist es wichtig, verwendete Variablen und verworfene zu unterscheiden. Wenn die Verwendung des Programms klar genug ist, ist es besser, namenlose Variablen als Platzhalter zu verwenden, als jeden Wert zu kennzeichnen, selbst wenn er nicht weiter verwendet wird. Überprüfen wir dies anhand eines Beispiels, dem "Free Memory Checker".
Beispiel - Free Memory Checker
Beim Überprüfen des freien Speichers/Swaps benötigen wir keine weiteren Informationen, die andere Ressourcen anzeigen. Um eine bessere Übersichtlichkeit zu erzielen, können Sie anonyme Variablen erstellen, um spezifische Speicherbereiche zu halten.
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 // Anonyme und unbenutzte werden als _ markiert
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 // Aufruf des Systemaufrufs 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}
Folglich werden Variablen ohne Bezeichnungen gelesen. Obwohl anonyme Werte tatsächlich in einer Struktur gespeichert werden, gibt es keine Bezeichnungen/lesbaren Markierungen im Code.
Schlussfolgerung
- Die Verwendung von Go's
syscallundunsafeist immer noch sicherer als C/CGo. - Wenn Sie ein großes Projekt schreiben, das leicht erweitert werden kann:
- Verwenden Sie keine anonymen Variablen; geben Sie den Elementen jeweils Namen.
- Wenn Sie ein Projekt mit begrenztem Verwendungszweck schreiben:
- Sie können anonyme Variablen verwenden, um Speicherbereiche zu halten, die tatsächlich ungenutzt sind.
- Go's
syscallist leistungsstark und modern für die Handhabung von Low-Level-Aufrufen.