GoSuda

Go syscall er en fremragende erstatning for I/O på lavt niveau

By Yunjin Lee
views ...

Sammenfatning

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 calls i Pure Go. Heldigvis er de fleste af C function calls fuldt genimplementeret i Go på en god og tidssvarende måde. Lad os se nærmere på det.

System Call

System call er en direkte anmodning til operativsystemet. Eftersom systemet normalt er skrevet i en rigid, gammeldags stil, da det kører direkte på hardware, skal vi overveje, at dets kald skal levere en streng og korrekt form for en anmodning. Så selvom vi ikke behøver visse variabler, skal vi stadig udfylde størrelsen uanset brug. Lad os undersøge 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  uint664
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 de aktuelle systeminformationer. Vi kan sammenligne denne kode med en lås og en nøgle.syscall.SYS_SYSINFO er en nøgle, der åbner en lås, som er inde i en kernel. Derfor er det vigtigt at bruge den korrekte nøgle til en lås. Hvad vil der ske, hvis vi bruger syscall.SYS_GETPID til dette kald? Dette er en nøgle til en lås, der indeholder Process ID. Dette vil forsøge at få et PID fra et område beregnet til systeminformation. Som et resultat kan ingen af informationerne læses korrekt; kaldet skal returneres som en failed state.

Nu skal vi vide, hvilke elementer der er indeholdt, og hvordan elementerne er ordnet. I den første plads i en lås 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 partielle binære data, medmindre vi har til hensigt at skrive low-level tricks.

Efter at have læst 64 bits binære data, er vi endelig på den anden plads. Det kan kun læses præcist, når vi har læst det foregående 64-bit store heltal.

Ved at gentage disse stringente og logiske forløb for at opnå korrekt information fra et system, kan vi korrekt håndtere læste data.

Hvordan man springer 'variabelnavne' over

Selvom vi ikke kan 'springe' variablerne selv over, er det vigtigt at skelne mellem anvendte variabler og de kasserede. Hvis brugen af programmet er klar nok, er det bedre at bruge navnløse variabler som pladsholdere end at navngive hver værdi, selvom de aldrig bliver brugt. Lad os se på dette med et eksempel, "Free Memory Checker".

Eksempel - Free Memory Checker

Når vi kontrollerer Free Memory/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  // anonymous, and unused ones are marked as _ (anonyme og ubrugte er markeret 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}

Som følge heraf læses variabler uden labels. Selvom anonyme værdier faktisk er gemt i en structure, er der ingen labels/læselige markeringer i koden.

Konklusion

  • Brug af Go's syscall og unsafe er stadig sikrere end C/CGo
  • Hvis du skriver et stort projekt, der let kan udvides:
    • Opret ikke anonyme variabler; navngiv hvert medlem.
  • Hvis du skriver et projekt, der har begrænset brug:
    • Du kan bruge anonyme variabler til at holde pladser, der faktisk ikke bruges.
  • Go's syscall er kraftfuld og moderne til at håndtere low-level calls

Læs Videre

syscall unsafe x/sys/unix