Go syscall er en fremragende erstatning for lavniveau I/O
Resume
Vi vil lære om direkte System Call i Go. Eftersom Go tilbyder strenge compiler errors og rigid GC, er det meget bedre at erstatte low-level kald i Ren Go. Heldigvis er de fleste af C function calls fuldt genimplementeret i Go, på en god og moderne måde. Lad os tage et kig på det.
System Call
System call er en direkte forespørgsel til operating system. Da systemet sædvanligvis er skrevet i en rigid, gammeldags stil, da det kører direkte på hardware, er vi nødt til at overveje, at dets kald skal levere en streng og korrekt form for en forespørgsel. Så selvom vi ikke behøver visse variabler, skal vi stadig udfylde størrelsen uanset brug. Lad os undersøge det med et fuldt fungerende eksempel.
Fuldt 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 eksempel inkluderer alle variabler og udskriver omfattende information om den aktuelle systeminformation.
Vi kan sammenligne denne kode som et skab og en nøgle.syscall.SYS_SYSINFO er en nøgle, der låser et skab op, som er inde i en kernel.
Derfor er det vigtigt at bruge den korrekte nøgle til et skab.
Hvad vil der ske, når vi bruger syscall.SYS_GETPID til dette kald?
Dette er en nøgle til et skab, der indeholder Process ID.
Dette vil forsøge at få et PID fra et område til systeminformation.
Som et resultat kan ingen af informationerne læses korrekt; kaldet skal returneres i en mislykket tilstand.
Nu skal vi vide, hvilke elementer der er indeholdt, og hvordan elementerne er ordnet. I den første plads i et skab har vi Uptime, med en størrelse på 2^64. Hvis vi forsøger at læse dette med 2^32, læses bitsekvensen ikke fuldt ud. Vi kan ikke bruge denne slags delvise binære data, medmindre vi har til hensigt at skrive low-level tricks.
Efter at have læst 64 bits binær data, er vi endelig på den anden plads. Den kan kun læses præcist, når vi har læst det forrige 64-bit store heltal.
Ved at gentage disse strenge og logiske forløb for at opnå korrekt information fra et system, kan vi håndtere læste data korrekt.
Hvordan man springer 'variabelnavne' over
Selvom vi ikke kan 'springe' variablerne selv over, er det vigtigt at skelne mellem brugte variabler og kasserede variabler. Hvis brugen af programmet er tilstrækkelig klar, er det bedre at bruge navnløse variabler som pladsholdere end at navngive hver værdi, selvom de ikke bruges fremover. Lad os undersøge dette med et eksempel, "Free Memory Checker" (Kontrol af Fri Hukommelse).
Eksempel - Kontrol af Fri Hukommelse
Når vi kontrollerer Fri Hukommelse/Swaps, behøver vi ikke anden information, der indikerer forskellige ressourcer. For at opnå bedre synlighed kan du oprette anonyme variabler til at holde specifikke pladser.
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 // anonyme og ubrugte markeres 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 læses variablerne uden labels. Selvom anonyme værdier faktisk gemmes i en struktur, er der ingen labels/læselige markeringer på koden.
Konklusion
- Brug af Go's
syscallogunsafeer stadig sikrere end C/CGo - Hvis du skriver et stort projekt, der let kan udvides:
- Opret ikke anonyme variabler; opret navne for hvert medlem.
- Hvis du skriver et projekt med begrænset brug:
- Du kan bruge anonyme variabler til at holde pladser, der faktisk er ubrugte.
- Go's
syscaller kraftfuld og moderne til at håndtere low-level kald