Go syscall on loistava korvaaja matalan tason I/O:lle
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- jaunsafe-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
syscallon tehokas ja moderni tapa käsitellä matalan tason kutsuja