GoSuda

Direkte Linux-Systemaufrufe in reinem Go mit syscall.Syscall und unsafe.Pointer

By Yunjin Lee views ...

Zusammenfassung

Wir werden etwas über direkte Systemaufrufe in Go lernen. Da Go strikte Compiler-Fehlermeldungen und einen rigiden GC bietet, ist es deutlich besser, low-level Aufrufe durch reines Go zu ersetzen. Glücklicherweise sind die meisten C-Funktionsaufrufe vollständig in Go neu implementiert, und zwar auf eine gute und zeitgemäße Weise. Schauen wir uns das an.

Systemaufruf

Ein Systemaufruf ist eine direkte Anfrage an das Betriebssystem. Da das System üblicherweise in einem strengen, altmodischen Stil geschrieben ist, weil es direkt auf der Hardware läuft, müssen wir berücksichtigen, dass sein Aufruf eine strikte und korrekte Form einer Anfrage liefern muss. Selbst wenn wir manche Variablen nicht benötigen, müssen wir deren Größe daher unabhängig von ihrer Verwendung ausfüllen. Sehen wir uns ein voll funktionsfähiges Beispiel an.

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 die aktuellen Systeminformationen aus. Wir können diesen Code mit einem Schloss und einem Schlüssel vergleichen.syscall.SYS_SYSINFO ist ein Schlüssel, der ein Schloss öffnet, das sich im Kernel befindet. Daher ist es wichtig, den richtigen Schlüssel für ein Schloss zu verwenden. Was passiert, wenn wir syscall.SYS_GETPID für diesen Aufruf verwenden? Dies ist ein Schlüssel für ein Schloss, das eine Prozess-ID enthält. Dies versucht, eine PID aus einem Speicherbereich 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 die Elemente angeordnet sind. Im ersten Fach eines Schlosses befindet sich Uptime mit einer Größe von 2^64. Wenn wir dies mit 2^32 lesen würden, wird die Bitfolge nicht vollständig gelesen. Wir können solche partiellen Binärdaten nicht verwenden, es sei denn, wir schreiben low-level Tricks.

Nach dem Lesen von 64 Bit Binärdaten befinden wir uns schließlich am zweiten Fach. Es kann nur dann korrekt gelesen werden, wenn wir zuvor die vorherige 64-Bit-Ganzzahl gelesen haben.

Durch die Wiederholung dieser strikten und logischen Abläufe erhalten wir die richtigen Informationen aus dem System, und wir können die gelesenen Daten ordnungsgemäß verarbeiten.

Wie man 'Variablennamen' überspringt

Auch wenn wir Variablen selbst nicht 'überspringen' können, ist es wichtig, verwendete und verworfene Variablen zu unterscheiden. Wenn der Zweck des Programms klar genug ist, ist es besser, namenlose Variablen als Platzhalter zu verwenden, anstatt jeden Wert zu benennen, auch wenn sie nicht dauerhaft verwendet werden. Sehen wir uns das an einem Beispiel an, dem "Free Memory Checker".

Beispiel - Free Memory Checker

Wenn wir freien Speicher bzw. Swap prüfen, benötigen wir keine anderen Informationen, die auf andere Ressourcen hinweisen. Um eine bessere Übersichtlichkeit zu erreichen, können Sie anonyme Variablen verwenden, um bestimmte Bereiche 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  // anonymous, and unused ones are marked as _
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 bzw. lesbaren Markierungen.

Fazit

  • Die Verwendung von Gos syscall und unsafe ist weiterhin sicherer als C/CGo
  • Wenn Sie ein großes Projekt schreiben, das leicht erweitert werden kann:
    • Verwenden Sie keine anonymen Variablen; geben Sie jedem Mitglied einen eigenen Namen.
  • Wenn Sie ein Projekt mit begrenztem Einsatzzweck schreiben:
    • Sie können anonyme Variablen verwenden, um Bereiche zu halten, die tatsächlich ungenutzt sind.
  • Gos syscall ist leistungsstark und modern für die Handhabung von low-level Aufrufen

Weiterführende Literatur

syscall unsafe x/sys/unix