GoSuda

Go syscall ist ein brillanter Ersatz für Low-Level I/O

By Yunjin Lee
views ...

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 2 64 2^{64} c90263519492728e7cc2d0ce840057b6 Wenn wir versuchen, dies mit 2 32 2^{32} 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 syscall und unsafe ist 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 syscall ist leistungsstark und modern für die Handhabung von Low-Level-Aufrufen.

Weiterführende Lektüre

syscall unsafe x/sys/unix