GoSuda

Go syscall on loistava korvaaja matalan tason I/O:lle

By Yunjin Lee
views ...

Yhteenveto

Tutustumme suoraan järjestelmäkutsuun (direct system call) Go-kielessä. Koska Go tarjoaa tiukkoja kääntäjävirheitä ja jäykän GC:n (Garbage Collection), on paljon parempi korvata matalan tason kutsut puhtaalla Go-kielellä (Pure Go). Onneksi suurin osa C-funktiokutsuista on täysin toteutettu uudelleen Go-kielessä, hyvällä ja nykyaikaisella tavalla. Tarkastellaanpa asiaa.

Järjestelmäkutsu (System Call)

Järjestelmäkutsu on suora pyyntö käyttöjärjestelmälle. Koska järjestelmä on yleensä kirjoitettu jäykällä, vanhanaikaisella tyylillä, koska se toimii suoraan laitteistolla (hardware), meidän on otettava huomioon, että sen kutsun on toimitettava tiukka ja oikea muoto pyynnöstä. Niinpä, vaikka emme tarvitsisikaan joitakin muuttujia, meidän on silti täytettävä koko riippumatta käytöstä. Tarkistetaan asia täysin toimivalla esimerkillä.

Täydellinen esimerkki

 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}

Tämä esimerkki sisältää kaikki muuttujat ja tulostaa kattavat tiedot nykyisestä järjestelmäinformaatiosta. Voimme verrata tätä koodia lukkoon ja avaimeen.syscall.SYS_SYSINFO on avain, joka avaa lukon, joka on ytimen (kernel) sisällä. Siksi oikean avaimen käyttäminen lukkoon on tärkeää. Mitä tapahtuu, kun käytämme syscall.SYS_GETPID tähän kutsuun? Tämä on avain lukkoon, joka sisältää prosessin tunnuksen (Process ID). Tämä yrittää saada PID:n järjestelmätietojen tilasta. Tämän seurauksena mitään tietoa ei voida lukea oikein; kutsu on palautettava epäonnistuneena tilana.

Nyt meidän on tiedettävä, mitä kohteita on sisällä ja miten kohteet on järjestetty. Lukon ensimmäisessä paikassa meillä on Uptime, jonka koko on 2^64. Jos yritämme lukea tämän 2^32:lla, bittisekvenssiä ei lueta kokonaan. Emme voi käyttää tällaisia osittaisia binäärejä, ellemme aio kirjoittaa matalan tason temppuja.

Luettuamme 64 bittiä binääristä dataa, olemme vihdoin toisessa paikassa. Se voidaan lukea tarkasti vain, kun olemme lukeneet edellisen 64-bittisen kokonaisluvun.

Toistamalla näitä tiukkoja ja loogisia kulkuja saadaksemme asianmukaisen tiedon järjestelmästä, voimme käsitellä luettua dataa asianmukaisesti.

Kuinka ohittaa 'muuttujanimet'

Vaikka emme voi 'ohittaa' itse muuttujia, on tärkeää erottaa toisistaan käytetyt muuttujat ja hylätyt muuttujat. Jos ohjelman käyttö on riittävän selkeää, on parempi käyttää nimettömiä muuttujia paikkamerkkeinä kuin nimetä jokainen arvo, vaikka niitä ei käytettäisikään koskaan. Tarkastellaan tätä esimerkillä "Vapaan muistin tarkistin" (Free Memory Checker).

Esimerkki - Vapaan muistin tarkistin

Kun tarkistetaan vapaata muistia/vaihtoja (Free Memory/Swaps), emme tarvitse muita tietoja, jotka osoittavat eri resursseja. Paremman näkyvyyden saavuttamiseksi voit luoda nimettömiä muuttujia pitämään tiettyjä tiloja.

 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  // nimettömät ja käyttämättömät on merkitty _
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}

Näin ollen muuttujat luetaan ilman tunnisteita. Vaikka nimettömät arvot todellisuudessa tallennetaan rakenteeseen, koodissa ei ole tunnisteita/luettavia merkkejä.

Johtopäätös

  • Go:n syscall- ja unsafe-käyttö on edelleen turvallisempaa kuin C/CGo
  • Jos kirjoitat valtavaa projektia, jota voidaan laajentaa helposti:
    • Älä tee nimettömiä muuttujia; anna jokaiselle jäsenelle nimi.
  • Jos kirjoitat projektia, jolla on rajoitettu käyttö:
    • Voit käyttää nimettömiä muuttujia pitämään tiloja, jotka ovat todella käyttämättömiä.
  • Go:n syscall on tehokas ja moderni tapa käsitellä matalan tason kutsuja

Lue lisää

syscall unsafe x/sys/unix