Go syscall är en lysande ersättning för lågnivå-I/O
Sammanfattning
Vi kommer att lära oss om direkta system call i Go. Eftersom Go erbjuder strikta kompilatorfel och rigid GC, är det mycket bättre att ersätta lågnivåkall i Ren Go. Lyckligtvis är de flesta av C-funktionskallen helt reimplementerade i Go, på ett bra och samtida sätt. Låt oss ta en titt på det.
System Call
System call är en direkt förfrågan till operativsystemet. Eftersom systemet vanligtvis är skrivet i en rigid, gammaldags stil då det körs direkt på hårdvara, måste vi beakta att dess anrop måste leverera en strikt och korrekt form av en förfrågan. Så, även om vi inte behöver vissa variabler, måste vi ändå fylla i storleken oavsett användning. Låt oss kontrollera med ett fullt fungerande exempel.
Fullständigt Exempel
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}
Detta exempel inkluderar alla variabler och skriver ut omfattande information om aktuell systeminformation.
Vi kan jämföra denna kod med ett skåp och en nyckel.syscall.SYS_SYSINFO är en nyckel som låser upp ett skåp som finns inuti en kernel.
Därför är det viktigt att använda rätt nyckel för ett skåp.
Vad kommer att hända när vi använder syscall.SYS_GETPID för detta anrop?
Detta är en nyckel till ett skåp som innehåller Process ID.
Detta kommer att försöka hämta ett PID från ett utrymme avsett för systeminformation.
Som ett resultat kan ingen av informationen läsas korrekt; anropet måste returneras som ett misslyckat tillstånd.
Nu måste vi veta vilka objekt som ingår och hur objekten är ordnade. I den första platsen i ett skåp har vi Uptime, med en storlek på 2^64. Om vi försöker läsa detta med 2^32, läses inte bitsekvensen fullständigt. Vi kan inte använda denna typ av partiella binärer om vi inte ska skriva lågnivåtrick.
Efter att ha läst 64 bitar binär data är vi äntligen på den andra platsen. Den kan endast läsas korrekt när vi har läst det föregående 64-bitars heltalet.
Genom att upprepa dessa strikta och logiska flöden för att erhålla korrekt information från ett system, kan vi hantera läst data på rätt sätt.
Hur man hoppar över 'variabelnamn'
Även om vi inte kan 'hoppa över' variabler i sig, är det viktigt att skilja mellan använda variabler och de som kasseras. Om användningen av programmet är tillräckligt tydlig, är det bättre att använda namnlösa variabler som platshållare än att märka varje värde även om de aldrig används. Låt oss kontrollera detta med ett exempel, "Free Memory Checker"
Exempel - Kontroll av Ledigt Minne
När vi kontrollerar Ledigt Minne/Swaps, behöver vi inte annan information som indikerar olika resurser. För att uppnå bättre synlighet kan du skapa anonyma variabler för att hålla specifika utrymmen.
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}
Följaktligen läses variabler utan etiketter. Även om anonyma värden faktiskt lagras i en struktur, finns det inga etiketter/läsbara märken på koden.
Slutsats
- Att använda Go:s
syscallochunsafeär fortfarande säkrare än C/CGo - Om du skriver ett stort projekt som enkelt kan expanderas:
- Skapa inte anonyma variabler; skapa namn för varje medlem.
- Om du skriver ett projekt som har begränsad användning:
- Du kan använda anonyma variabler för att hålla utrymmen som faktiskt inte används.
- Go:s
syscallär kraftfullt och modernt för att hantera lågnivåkall