GoSuda

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

By Yunjin Lee
views ...

Yhteenveto

Tutustumme suoraan System call -kutsuun Go-kielessä. Koska Go tarjoaa tiukat kääntäjävirheet ja jäykän GC:n, on paljon parempi korvata matalan tason kutsut puhtaassa Go:ssa (Pure Go). Onneksi suurin osa C-funktiokutsuista on toteutettu Go:ssa uudelleen täysin, hyvällä ja nykyaikaisella tavalla. Tarkastellaanpa sitä.

System Call

System call on suora pyyntö käyttöjärjestelmälle. Koska järjestelmä on yleensä kirjoitettu jäykkään, vanhanaikaiseen tyyliin, koska se suoritetaan suoraan laitteistolla, meidän on otettava huomioon, että sen kutsun on toimitettava tiukka ja oikea muoto pyynnölle. Joten, vaikka emme tarvitse joitain muuttujia, meidän on silti täytettävä koko riippumatta käytöstä. Tarkastellaan tätä täysin toimivan esimerkin avulla.

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ätiedosta. Voimme verrata tätä koodia lukkoon ja avaimeen.syscall.SYS_SYSINFO on avain, joka avaa ytimen sisällä olevan lukon. Siksi oikean avaimen käyttäminen lukkoon on tärkeää. Mitä tapahtuu, kun käytämme syscall.SYS_GETPID:tä 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; kutsun on palattava epäonnistuneena tilana.

Nyt meidän on tiedettävä, mitkä kohteet sisältyvät 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, bittijonoa ei lueta kokonaan. Emme voi käyttää tällaisia osittaisia binäärejä, ellei ole tarkoituksena kirjoittaa matalan tason temppuja.

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

Toistamalla nämä tiukat ja loogiset kulut saadaksemme asianmukaisen tiedon järjestelmästä, voimme käsitellä luettuja tietoja asianmukaisesti.

Kuinka ohittaa 'muuttujanimet'

Vaikka emme voi 'ohittaa' itse muuttujia, on tärkeää erottaa käytetyt muuttujat hylätyistä. Jos ohjelman käyttö on riittävän selkeää, on parempi käyttää nimettömiä muuttujia paikkamerkkeinä kuin merkitä jokaista arvoa, vaikka niitä ei käytettäisi koskaan. Tarkastellaan tätä esimerkin avulla, "Vapaan muistin tarkistaja" (Free Memory Checker).

Esimerkki - Vapaan muistin tarkistaja

Kun tarkistetaan vapaata muistia/vaihtotiedostoja (Free Memory/Swaps), emme tarvitse muita tietoja, jotka osoittavat erilaisia resursseja. Paremman näkyvyyden saavuttamiseksi voit luoda anonyymejä 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  // anonymous, and unused ones are marked as _
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 anonyymit arvot tallennetaan itse asiassa 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 suurta projektia, jota voidaan helposti laajentaa:
    • Älä tee anonyymejä muuttujia; anna jokaiselle jäsenelle nimi.
  • Jos kirjoitat projektia, jolla on rajallinen käyttö:
    • Voit käyttää anonyymejä muuttujia pitämään tiloja, joita ei itse asiassa käytetä.
  • Go:n syscall on tehokas ja moderni käsittelemään matalan tason kutsuja

Lue lisää

syscall unsafe x/sys/unix