GoSuda

A Go syscall a low-level I/O ragyogó helyettesítője.

By Yunjin Lee
views ...

Összefoglalás

Megismerkedünk a közvetlen rendszerhívással Go nyelven. Mivel a Go szigorú fordítóprogram-hibákat és merev GC-t kínál, sokkal jobb a low-level hívásokat tiszta Go-ban helyettesíteni. Szerencsére a legtöbb C függvényhívás teljes mértékben újraimplementálva lett Go nyelven, megfelelő és korszerű módon. Vessünk rá egy pillantást.

Rendszerhívás (System Call)

A rendszerhívás közvetlen kérés az operációs rendszerhez. Mivel a rendszert általában merev, régimódi stílusban írják, mivel közvetlenül a hardveren fut, figyelembe kell vennünk, hogy a hívásának a kérés szigorú és korrekt formáját kell továbbítania. Tehát, még ha nincs is szükségünk néhány változóra, a méretet akkor is ki kell töltenünk, a felhasználástól függetlenül. Nézzünk meg egy teljesen működő példát.

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 a jelenlegi rendszerinformációkról. Összehasonlíthatjuk ezt a kódot egy zárral és egy kulccsal. A syscall.SYS_SYSINFO egy kulcs, amely kinyit egy zárat, amely a kernelen belül található. Ezért fontos a megfelelő kulcs használata a zárhoz. Mi történik, ha syscall.SYS_GETPID-et használunk ehhez a híváshoz? Ez egy kulcs ahhoz a zárhoz, amely a Process ID-t tartalmazza. Ez megpróbálja lekérni a PID-et a rendszerinformációk számára kijelölt helyről. Ennek eredményeként egyetlen információ sem olvasható be helyesen; a hívásnak sikertelen állapotban kell visszatérnie.

Most tudnunk kell, hogy mely elemeket tartalmazza, és hogyan vannak sorrendben az elemek. A zár első nyílásában van az Uptime, 2^64 mérettel. Ha ezt 2^32-vel próbáljuk beolvasni, a bitsorozat nem olvasható be teljesen. Nem használhatjuk az ilyen típusú részleges bináris adatokat, hacsak nem low-level trükköket fogunk írni.

64 bit bináris adat beolvasása után végre a második nyílásnál tartunk. Csak akkor olvasható be pontosan, ha beolvastuk az előző 64 bites méretű egészet.

Ezeknek a szigorú és logikai folyamatoknak a megismétlésével, hogy megfelelő információt szerezzünk a rendszerből, megfelelően tudjuk kezelni a beolvasott adatokat.

Hogyan hagyjuk figyelmen kívül 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 elvetetteket. Ha a program felhasználása kellően egyértelmű, jobb névtelen változókat használni helykitöltőként, mint minden értéket felcímkézni, még akkor is, ha soha nem kerülnek felhasználásra. Nézzük meg ezt egy példával, a "Free Memory Checker"-rel (Szabad memória ellenőrző).

Példa - Szabad memória ellenőrző

Amikor a Szabad Memóriát/Swap-et ellenőrizzük, nincs szükségünk más információra, amely eltérő erőforrásokat jelez. A jobb átláthatóság érdekében névtelen változókat hozhat létre, hogy megtartsák a specifikus helyeket.

 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 _ (a névtelen és nem használtakat _ jelöli)
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}

Következésképpen a változók címkék nélkül kerülnek beolvasásra. Bár a névtelen értékek ténylegesen tárolódnak egy struktúrában, a kódon nincsenek címkék/olvasható jelölések.

Következtetés

  • A Go syscall és unsafe használata még mindig biztonságosabb, mint a C/CGo
  • Ha egy hatalmas projektet ír, amelyet könnyen ki lehet bővíteni:
    • Ne hozzon létre névtelen változókat; adjon nevet minden tagnak.
  • Ha olyan projektet ír, amely korlátozott felhasználású:
    • Használhat névtelen változókat az olyan helyek megtartására, amelyek ténylegesen kihasználatlanok.
  • A Go syscall hatékony és modern a low-level hívások kezelésére

További olvasnivaló

syscall unsafe x/sys/unix