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 strikte Compilerfehler und eine rigide GC bietet, ist es wesentlich vorteilhafter, Low-Level-Aufrufe in reinem Go zu ersetzen. Glücklicherweise sind die meisten C-Funktionsaufrufe in Go vollständig und auf zeitgemäße Weise reimplementiert. Betrachten wir dies genauer.

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 bestimmte Variablen nicht benötigen, müssen wir 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	_, _, 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}

Dieses Beispiel enthält alle Variablen und gibt umfangreiche Informationen über den aktuellen Systemzustand aus. Wir können diesen Code als Schließfach und Schlüssel vergleichen.syscall.SYS_SYSINFO ist ein Schlüssel, der ein Schließfach im Kernel öffnet. Daher ist die Verwendung des richtigen 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 Prozess-ID enthält. Dies würde versuchen, eine PID aus einem Bereich für Systeminformationen abzurufen. Infolgedessen können keine der Informationen korrekt gelesen werden; der Aufruf muss als fehlgeschlagen zurückgegeben werden.

Nun müssen wir wissen, welche Elemente enthalten sind und wie sie geordnet sind. Im ersten Bereich eines Schließfachs haben wir die Uptime mit einer Größe von 2^64. Wenn wir dies mit 2^32 lesen wollen, wird die Bitsequenz nicht vollständig gelesen. 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 Bereich. Dieser kann nur dann genau gelesen werden, wenn wir die vorherige 64-Bit-Ganzzahl gelesen haben.

Durch die Wiederholung dieser strikten und logischen Abläufe zur Gewinnung korrekter Informationen aus einem System 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 Variablen zu unterscheiden. Wenn die Verwendung des Programms klar genug ist, ist es besser, namenlose Variablen als Platzhalter zu verwenden, anstatt jeden Wert zu kennzeichnen, selbst wenn er niemals verwendet wird. Überprüfen wir dies anhand eines Beispiels, dem "Free Memory Checker".

Beispiel – Freier Speicher-Checker

Beim Überprüfen von freiem Speicher/Swaps benötigen wir keine anderen Informationen, die unterschiedliche Ressourcen anzeigen. Um eine bessere Übersichtlichkeit zu erzielen, können Sie anonyme Variablen erstellen, um bestimmte Bereiche zu belegen.

 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 ungenutzte werden als _ gekennzeichnet
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}

Folglich werden Variablen ohne Bezeichnungen gelesen. Obwohl anonyme Werte tatsächlich in einer Struktur gespeichert werden, gibt es im Code keine Bezeichnungen/lesbaren Markierungen.

Fazit

  • Die Verwendung von Go's syscall und unsafe ist immer noch sicherer als C/CGo.
  • Wenn Sie ein umfangreiches Projekt schreiben, das leicht erweitert werden kann:
    • Erstellen Sie keine anonymen Variablen; benennen Sie jedes Element explizit.
  • Wenn Sie ein Projekt mit begrenzter Nutzung schreiben:
    • Sie können anonyme Variablen verwenden, um ungenutzte Bereiche zu belegen.
  • Go's syscall ist leistungsfähig und modern für die Handhabung von Low-Level-Aufrufen.

Weiterführende Informationen

syscall unsafe x/sys/unix