GoSuda

A Go syscall kiválóan helyettesíti az alacsony szintű I/O-t.

By Yunjin Lee
views ...

Ö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 és unsafe haszná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 syscall hatékony és modern az alacsony szintű hívások kezelésére

További olvasmányok

syscall unsafe x/sys/unix