Go syscall er en strålende erstatning for lavnivå I/O
Sammendrag
Vi skal lære om direkte system call i Go. Siden Go tilbyr strenge kompileringsfeil og rigid GC, er det mye bedre å erstatte low-level calls i Pure Go. Heldigvis er de fleste C function calls fullstendig reimplementert i Go, på en god og moderne måte. La oss se nærmere på det.
System Call
System call er en direkte forespørsel til operativsystemet. Siden systemet vanligvis er skrevet i en rigid, gammeldags stil ettersom det kjører direkte på maskinvare, må vi vurdere at dets call må levere en streng og korrekt form for en forespørsel. Så selv om vi ikke trenger visse variabler, må vi likevel fylle ut størrelsen uavhengig av bruk. 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 eksemplet inkluderer alle variabler, og skriver ut omfattende informasjon om gjeldende systeminformasjon.
Vi kan sammenligne denne koden, som et skap og en nøkkel.syscall.SYS_SYSINFO er en nøkkel som låser opp et skap som er inne i en kernel.
Derfor er det viktig å bruke riktig nøkkel til et skap.
Hva vil skje når vi bruker syscall.SYS_GETPID for dette call-et?
Dette er en nøkkel til et skap 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; call-et må returneres som en feilet tilstand.
Nå må vi vite hvilke elementer som er inkludert, og hvordan elementene er ordnet. I den første plassen i et skap 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 slike delvise binære filer med mindre vi skal skrive low-level triks.
Etter å ha lest 64 bits binære data, er vi endelig på den andre plassen. Den kan bare leses nøyaktig når vi har lest et tidligere 64-biters heltall.
Ved å gjenta disse strenge og logiske flytene for å hente riktig informasjon fra et system, kan vi håndtere leste data på en korrekt måte.
Hvordan hoppe over 'variabelnavn'
Selv om vi ikke kan 'hoppe over' variabler 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 å merke hver verdi selv om de aldri blir brukt. La oss sjekke dette med et eksempel, "Free Memory Checker".
Eksempel - Free Memory Checker
Når vi sjekker Free Memory/Swaps, trenger vi ikke annen informasjon som indikerer forskjellige 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 // Uptime
11 _ [3]uint64 // Loads
12 Totalram uint64
13 Freeram uint64
14 Sharedram uint64
15 Bufferram uint64
16 Totalswap uint64
17 Freeswap uint64
18 _ uint16 // anonyme, og ubrukte er merket som _ // Procs
19 _ uint16 // Pad
20 _ [4]byte // padding
21 _ uint64 // Totalhigh
22 _ uint64 // Freehigh
23 MemUnit uint32
24 _ [4]byte // padding
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 leses variabler uten etiketter. Selv om anonyme verdier faktisk lagres i en struktur, er det ingen etiketter/lesbare merker i koden.
Konklusjon
- Bruk av Go's
syscallogunsafeer fortsatt sikrere enn C/CGo - Hvis du skriver et stort prosjekt som enkelt kan utvides:
- Ikke lag anonyme variabler; gi hvert medlem et navn.
- Hvis du skriver et prosjekt som har begrenset bruk:
- Du kan bruke anonyme variabler til å holde plasser som faktisk er ubrukte.
- Go's
syscaller kraftig og moderne for å håndtere low-level calls