GoSuda

Go syscall je vynikajúca náhrada za I/O nízkej úrovne.

By Yunjin Lee
views ...

Súhrn

Oboznámime sa s priamym systémovým volaním v Go. Keďže Go ponúka prísne chyby kompilátora a rigidný GC, je omnoho lepšie nahradiť volania nízkej úrovne v čistom Go (Pure Go). Našťastie, väčšina volaní C funkcií je kompletne re-implementovaná v Go, a to dobrým a moderným spôsobom. Pozrime sa na to.

Systémové volanie

Systémové volanie je priama požiadavka na operačný systém. Keďže systém je zvyčajne napísaný v rigidnom, staromódnom štýle, pretože beží priamo na hardvéri, musíme zvážiť, že jeho volanie musí doručiť presnú a správnu formu požiadavky. Takže, aj keď nepotrebujeme niektoré premenné, stále musíme vyplniť veľkosť bez ohľadu na použitie. Pozrime sa na plne funkčný príklad.

Úplný prí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	// Volanie syscall.Syscall s parametrami pre SYS_SYSINFO
29	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
30	if errno != 0 {
31		// Vypíše chybu, ak syscall zlyhal
32		fmt.Println("sysinfo syscall failed:", errno)
33		return
34	}
35
36	scale := float64(1 << 16)
37	// Vypíše čas prevádzky
38	fmt.Printf("Uptime: %d seconds\n", info.Uptime)
39	// Vypíše priemerné zaťaženie
40	fmt.Printf("Load Average: %.2f %.2f %.2f\n",
41		float64(info.Loads[0])/scale,
42		float64(info.Loads[1])/scale,
43		float64(info.Loads[2])/scale)
44	// Vypíše informácie o pamäti
45	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
46		info.Totalram*uint64(info.MemUnit)/1024/1024,
47		info.Freeram*uint64(info.MemUnit)/1024/1024,
48		info.Bufferram*uint64(info.MemUnit)/1024/1024)
49	// Vypíše informácie o Swape
50	fmt.Printf("Swap: total=%d MB free=%d MB\n",
51		info.Totalswap*uint64(info.MemUnit)/1024/1024,
52		info.Freeswap*uint64(info.MemUnit)/1024/1024)
53	// Vypíše počet procesov
54	fmt.Printf("Processes: %d\n", info.Procs)
55}

Tento príklad zahŕňa všetky premenné a vypisuje rozsiahle informácie o aktuálnom systéme. Tento kód môžeme prirovnať k skrinke a kľúču.syscall.SYS_SYSINFO je kľúč, ktorý odomyká skrinku, ktorá je vo vnútri jadra. Preto je dôležité použiť správny kľúč pre skrinku. Čo sa stane, ak pre toto volanie použijeme syscall.SYS_GETPID? Toto je kľúč pre skrinku, ktorá obsahuje ID procesu (Process ID). Toto sa pokúsi získať PID z priestoru určeného pre systémové informácie. V dôsledku toho sa žiadna z informácií nedá prečítať správne; volanie musí byť vrátené ako neúspešný stav.

Teraz potrebujeme vedieť, ktoré položky sú obsiahnuté a ako sú usporiadané. V prvom slote skrinky máme Uptime, s veľkosťou 2^64. Ak sa to pokúsime prečítať s 2^32, bitová sekvencia sa neprečíta úplne. Nemôžeme použiť takéto čiastočné binárne dáta, pokiaľ nebudeme písať triky nízkej úrovne.

Po prečítaní 64 bitov binárnych dát sa konečne dostávame na druhý slot. Dá sa prečítať presne len vtedy, ak sme prečítali predchádzajúce 64-bitové celé číslo.

Opakovaním týchto prísnych a logických postupov na získanie správnych informácií zo systému môžeme správne spracovať prečítané dáta.

Ako preskočiť 'názvy premenných'

Aj keď nemôžeme 'preskočiť' samotné premenné, je dôležité rozlišovať použité premenné a tie, ktoré boli zahodené. Ak je použitie programu dostatočne jasné, je lepšie použiť bezmenné premenné ako zástupné symboly, než označovať každú hodnotu, aj keď nie je nikdy použitá. Pozrime sa na to na príklade "Kontrola voľnej pamäte" (Free Memory Checker).

Príklad - Kontrola voľnej pamäte

Pri kontrole voľnej pamäte/Swaps nepotrebujeme iné informácie, ktoré naznačujú iné zdroje. Pre dosiahnutie lepšej prehľadnosti môžete použiť anonymné premenné na uchovanie špecifických priestorov.

 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	// anonymné a nepoužité sú označené ako _
19	_         uint16  
20	_         uint16  
21	_         [4]byte 
22	_         uint64  
23	_         uint64  
24	MemUnit   uint32
25	_         [4]byte
26}
27
28func main() {
29	var info sysinfo_t
30	// Volanie syscall.Syscall s parametrami pre SYS_SYSINFO
31	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
32	if errno != 0 {
33		// Vypíše chybu, ak syscall zlyhal
34		fmt.Println("sysinfo syscall failed:", errno)
35		return
36	}
37
38	// Vypíše informácie o pamäti
39	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
40		info.Totalram*uint64(info.MemUnit)/1024/1024,
41		info.Freeram*uint64(info.MemUnit)/1024/1024,
42		info.Bufferram*uint64(info.MemUnit)/1024/1024)
43	// Vypíše informácie o Swape
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}

V dôsledku toho sú premenné čítané bez označenia. Hoci sú anonymné hodnoty skutočne uložené v štruktúre, v kóde nie sú žiadne štítky/čitateľné značky.

Záver

  • Používanie syscall a unsafe v Go je stále bezpečnejšie ako C/CGo
  • Ak píšete rozsiahly projekt, ktorý sa dá ľahko rozšíriť:
    • Nevytvárajte anonymné premenné; vytvorte názvy pre každého člena.
  • Ak píšete projekt, ktorý má obmedzené použitie:
    • Môžete použiť anonymné premenné na uchovanie priestorov, ktoré sa skutočne nepoužívajú.
  • syscall v Go je výkonný a moderný na spracovanie volaní nízkej úrovne

Čítajte ďalej

syscall unsafe x/sys/unix