GoSuda

Go syscall je skvělou náhradou nízkoúrovňového I/O

By Yunjin Lee
views ...

Souhrn

Seznámíme se s přímým systémovým voláním v Go. Jelikož Go nabízí striktní chyby kompilátoru a rigidní GC, je mnohem lepší nahradit nízkoúrovňová volání v čistém Go (Pure Go). Naštěstí je většina volání funkcí C plně reimplementována v Go, a to dobrým a moderním způsobem. Pojďme se na to podívat.

Systémové volání

Systémové volání je přímý požadavek na operační systém. Vzhledem k tomu, že systém je obvykle napsán rigidním, staromódním stylem, protože běží přímo na hardwaru, musíme vzít v úvahu, že jeho volání musí poskytovat striktní a správnou formu požadavku. Takže, i když nepotřebujeme některé proměnné, stále musíme vyplnit velikost bez ohledu na použití. Pojďme se podívat na plně funkční příklad.

Úplný příklad

 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}

Tento příklad zahrnuje všechny proměnné a tiskne rozsáhlé informace o aktuálním stavu systému. Tento kód můžeme přirovnat k zámku a klíči.syscall.SYS_SYSINFO je klíč, který odemyká zámek, který je uvnitř jádra. Proto je důležité použít správný klíč k zámku. Co se stane, když pro toto volání použijeme syscall.SYS_GETPID? Toto je klíč k zámku, který obsahuje ID procesu (Process ID). Tím se pokusíme získat PID z prostoru pro systémové informace. V důsledku toho nelze žádnou z informací správně přečíst; volání musí být vráceno jako neúspěšný stav.

Nyní potřebujeme vědět, které položky jsou obsaženy a jak jsou uspořádány. V prvním slotu zámku máme Uptime s velikostí 2^64. Pokud se to pokusíme přečíst s 2^32, bitová sekvence není plně přečtena. Nemůžeme použít tento druh částečných binárních dat, pokud nebudeme psát nízkoúrovňové triky.

Po přečtení 64 bitů binárních dat jsme konečně na druhém slotu. Může být přesně přečten pouze tehdy, když jsme přečetli předchozí 64bitové celé číslo.

Opakováním těchto striktních a logických toků pro získání správných informací ze systému můžeme správně zpracovat přečtená data.

Jak přeskočit 'názvy proměnných'

I když nemůžeme 'přeskočit' proměnné samotné, je důležité rozlišovat použité proměnné a ty, které jsou zahozeny. Pokud je použití programu dostatečně jasné, je lepší použít bezejmenné proměnné jako zástupné symboly než označovat každou hodnotu, i když se nikdy nepoužijí. Pojďme se na to podívat na příkladu "Kontrola volné paměti" (Free Memory Checker).

Příklad - Kontrola volné paměti

Při kontrole volné paměti/výměn (Free Memory/Swaps) nepotřebujeme další informace, které by naznačovaly jiné zdroje. Pro dosažení lepší viditelnosti můžete vytvořit anonymní proměnné, které budou držet specifické prostory.

 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  // anonymní a nepoužité jsou označeny jako _
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}

V důsledku toho jsou proměnné čteny bez štítků. Přestože jsou anonymní hodnoty skutečně uloženy do struktury, v kódu nejsou žádné štítky/čitelná označení.

Závěr

  • Použití Go syscall a unsafe je stále bezpečnější než C/CGo
  • Pokud píšete velký projekt, který lze snadno rozšířit:
    • Nepoužívejte anonymní proměnné; pojmenujte každý člen.
  • Pokud píšete projekt, který má omezené použití:
    • Můžete použít anonymní proměnné k držení prostorů, které jsou ve skutečnosti nepoužité.
  • Go syscall je výkonné a moderní pro zpracování nízkoúrovňových volání

Další četba

syscall unsafe x/sys/unix