Go syscall er en strålende erstatning for lavnivå I/O
Sammendrag
Vi skal lære om direkte systemkall i Go. Siden Go tilbyr strenge compiler errors og rigid GC (Garbage Collection), er det mye bedre å erstatte lavnivåkall med Pure Go. Heldigvis er de fleste C-funksjonskall fullstendig reimplementert i Go, på en god og tidsriktig måte. La oss se nærmere på det.
Systemkall
Systemkall er en direkte forespørsel til operativsystemet. Siden systemet vanligvis er skrevet i en rigid, gammeldags stil ettersom det kjører direkte på maskinvaren, må vi vurdere at kallet må levere en streng og korrekt form for en forespørsel. Så, selv om vi ikke trenger noen variabler, må vi likevel fylle ut størrelsen uavhengig av bruken. La oss sjekke med et fullt fungerende eksempel.
Fullt eksempel
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}
Dette eksempelet inkluderer alle variabler, og skriver ut omfattende informasjon om gjeldende systeminformasjon.
Vi kan sammenligne denne koden med en lås og en nøkkel.syscall.SYS_SYSINFO er en nøkkel som låser opp en lås som befinner seg inne i en kernel.
Derfor er det viktig å bruke riktig nøkkel til låsen.
Hva vil skje når vi bruker syscall.SYS_GETPID for dette kallet?
Dette er en nøkkel til en lås som inneholder Process ID.
Dette vil forsøke å hente en PID fra et område for systeminformasjon.
Som et resultat kan ingen av informasjonen leses korrekt; kallet må returneres som en feilet tilstand.
Nå må vi vite hvilke elementer som er inkludert, og hvordan elementene er ordnet. I det første sporet av en lås har vi Uptime, med en størrelse på 2^64. Hvis vi prøver å lese dette med 2^32, blir ikke bitsekvensen fullstendig lest. Vi kan ikke bruke denne typen partielle binære data med mindre vi skal skrive lavnivå-triks.
Etter å ha lest 64 bits med binær data, er vi endelig på det andre sporet. Det kan kun leses nøyaktig når vi har lest det forrige 64-biters heltallsverdien.
Ved å gjenta disse strenge og logiske flytene for å innhente riktig informasjon fra et system, kan vi behandle leste data på en forsvarlig måte.
Hvordan hoppe over 'variabelnavn'
Selv om vi ikke kan 'hoppe over' variablene i seg selv, er det viktig å skille mellom brukte variabler og forkastede variabler. Hvis bruken av programmet er tydelig nok, er det bedre å bruke navnløse variabler som plassholdere enn å navngi hver verdi selv om de aldri blir brukt. La oss sjekke dette med et eksempel, "Free Memory Checker" (Sjekk av ledig minne).
Eksempel - Free Memory Checker
Når vi sjekker ledig minne/Swaps, trenger vi ikke annen informasjon som indikerer andre ressurser. For å oppnå bedre synlighet, kan du lage anonyme variabler for å holde spesifikke plasser.
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 _ (anonyme og ubrukte er merket som _)
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}
Følgelig blir variabler lest uten etiketter. Selv om anonyme verdier faktisk lagres i en struktur, er det ingen etiketter/lesbare markeringer i koden.
Konklusjon
- Bruk av Go's
syscallogunsafeer fortsatt tryggere enn C/CGo - Hvis du skriver et stort prosjekt som lett kan utvides:
- Ikke lag anonyme variabler; lag egne navn for medlemmene.
- Hvis du skriver et prosjekt som har begrenset bruk:
- Du kan bruke anonyme variabler for å holde plasser som faktisk ikke brukes.
- Go's
syscaller kraftfull og moderne for å håndtere lavnivåkall