Go syscall je brilantní náhradou nízkoúrovňového I/O
Souhrn
Seznámíme se s přímým voláním systému (system call) v jazyce Go. Jelikož Go nabízí striktní chyby kompilátoru a rigidní GC (Garbage Collector), je mnohem lepší nahradit nízkoúrovňová volání v Pure Go. Naštěstí je většina volání funkcí jazyka C plně reimplementována v Go, a to dobrým a moderním způsobem. Pojďme se na to podívat.
Volání Systému (System Call)
Volání systému je přímý požadavek na operační systém. Jelikož je systém 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í dodat 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 to zkontrolovat na plně funkčním příkladu.
Kompletní 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 ke skříňce a klíči.syscall.SYS_SYSINFO je klíč, který odemyká skříňku, jež se nachází uvnitř jádra.
Proto je důležité použít správný klíč ke skříňce.
Co se stane, když pro toto volání použijeme syscall.SYS_GETPID?
Toto je klíč ke skříňce, která obsahuje ID procesu (Process ID).
Tento pokus se bude snažit získat PID z prostoru určeného pro systémové informace.
V důsledku toho nelze žádnou informaci 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 skříňky máme Uptime, o velikosti 2^64. Pokud se to pokusíme přečíst s 2^32, bitová sekvence není plně přečtena. Tyto druhy částečných binárních dat nemůžeme použít, pokud nehodláme psát nízkoúrovňové triky.
Po přečtení 64 bitů binárních dat se konečně dostáváme k druhému 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ů k získání správné informace ze systému můžeme řádně zpracovat přečtená data.
Jak přeskočit 'názvy proměnných'
I když nemůžeme 'přeskočit' samotné proměnné, je důležité rozlišovat použité proměnné a ty, které byly zahozeny. Pokud je použití programu dostatečně jasné, je lepší použít bezejmenné proměnné jako zástupné symboly než pojmenovávat každou hodnotu, i když se nikdy nepoužije. Pojďme to zkontrolovat na příkladu "Kontrola Volné Paměti" ("Free Memory Checker").
Příklad - Kontrola Volné Paměti
Při kontrole volné paměti/výměnné paměti (Swaps) nepotřebujeme jiné informace, které by naznačovaly různé zdroje. Pro dosažení lepší přehlednosti můžete vytvořit anonymní proměnné, které obsadí 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 // 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}
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é značky.
Závěr
- Použití
syscallaunsafev Go je stále bezpečnější než C/CGo - Pokud píšete obrovský projekt, který lze snadno rozšířit:
- Nevytvářejte anonymní proměnné; vytvořte pro členy jednotlivé názvy.
- Pokud píšete projekt, který má omezené použití:
- Můžete použít anonymní proměnné k obsazení prostorů, které se ve skutečnosti nepoužívají.
syscallv Go je výkonné a moderní pro zpracování nízkoúrovňových volání