A Go syscall kiválóan helyettesíti az alacsony szintű I/O-t.
Összefoglalás
Megismerkedünk a közvetlen rendszerhívással Go nyelven. Mivel a Go szigorú fordítóhibákat és merev GC-t kínál, sokkal jobb az alacsony szintű hívásokat tiszta Go-ban helyettesíteni. Szerencsére a legtöbb C függvényhívás teljes mértékben újra van implementálva Go-ban, jó és modern módon. Vessünk rá egy pillantást.
Rendszerhívás
A rendszerhívás közvetlen kérés az operációs rendszerhez. Mivel a rendszer általában merev, régimódi stílusban íródott, mivel közvetlenül hardveren fut, figyelembe kell vennünk, hogy a hívásának szigorú és korrekt formájú kérést kell szolgáltatnia. Tehát, még ha nincs is szükségünk bizonyos változókra, akkor is ki kell töltenünk a méretet, függetlenül a felhasználástól. Nézzük meg egy teljesen működő példával.
Teljes példa
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}
Ez a példa tartalmazza az összes változót, és kiterjedt információkat nyomtat az aktuális rendszerről.
Ezt a kódot egy szekrényhez és egy kulcshoz hasonlíthatjuk.
A syscall.SYS_SYSINFO egy kulcs, amely felold egy szekrényt, ami a kernelben található.
Ezért fontos a megfelelő kulcs használata egy szekrényhez.
Mi történik, ha syscall.SYS_GETPID-et használunk ehhez a híváshoz?
Ez egy kulcs egy olyan szekrényhez, amely a Process ID-t tartalmazza.
Ez megpróbálja lekérdezni egy PID-et a rendszerinformációk számára fenntartott helyről.
Ennek eredményeként az információk egyike sem olvasható helyesen; a hívást sikertelen állapotként kell visszatéríteni.
Most tudnunk kell, hogy mely elemeket tartalmazza, és hogyan vannak rendezve az elemek. Egy szekrény első nyílásában van az Uptime, 2^64 méretű. Ha 2^32-vel próbáljuk elolvasni, a bitsorozat nem olvasható be teljesen. Nem használhatunk ilyen típusú részleges binárisokat, hacsak nem alacsony szintű trükköket írunk.
Miután elolvastuk a 64 bites bináris adatokat, végre a második nyílásban vagyunk. Csak akkor olvasható be pontosan, ha elolvastuk az előző 64 bites méretű egész számot.
Azáltal, hogy megismételjük ezeket a szigorú és logikus folyamatokat a rendszerből származó megfelelő információk megszerzéséhez, helyesen tudjuk kezelni az olvasott adatokat.
Hogyan hagyjuk ki a 'változóneveket'
Bár magukat a változókat nem tudjuk 'kihagyni', fontos megkülönböztetni a használt változókat és az eldobottakat. Ha a program használata elég egyértelmű, jobb névtelen változókat használni helyőrzőként, mint minden értéket felcímkézni, még akkor is, ha soha nem használják őket. Nézzük meg ezt egy példával, az "Ingyenes memória ellenőrzővel".
Példa - Ingyenes memória ellenőrző
Amikor az Ingyenes memóriát/Swaps-et ellenőrizzük, nincs szükségünk más információra, amely különböző erőforrásokat jelez. A jobb láthatóság elérése érdekében névtelen változókat hozhat létre, hogy speciális helyeket tartsanak.
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 // névtelen, és a nem használtak _-vel vannak jelölve
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}
Ennek következtében a változók címkék nélkül olvashatók be. Bár a névtelen értékek ténylegesen egy struktúrába vannak tárolva, nincsenek címkék/olvasható jelölések a kódon.
Összegzés
- A Go
syscallésunsafehasználata még mindig biztonságosabb, mint a C/CGo - Ha egy hatalmas projektet ír, amely könnyen bővíthető:
- Ne hozzon létre névtelen változókat; minden tagnak adjon nevet.
- Ha olyan projektet ír, amely korlátozottan használható:
- Használhat névtelen változókat a ténylegesen nem használt helyek tárolására.
- A Go
syscallhatékony és modern az alacsony szintű hívások kezelésére