Suorat Linux-järjestelmäkutsut puhtaassa Go-kielessä syscall.Syscall- ja unsafe.Pointer-funktioiden avulla

Yhteenveto
Opimme suoran järjestelmäkutsun käytön Go-kielessä. Koska Go tarjoaa tiukat kääntäjävirheet ja jäykän roskienkeruun (GC), on paljon parempi korvata matalan tason kutsut puhtaalla Go-koodilla. Onneksi suurin osa C-funktiokutsuista on toteutettu uudelleen kokonaan Go-kielellä, hyvällä ja nykyaikaisella tavalla. Tutustutaan siihen.
Järjestelmäkutsu
Järjestelmäkutsu on suora pyyntö käyttöjärjestelmälle. Koska järjestelmä on yleensä kirjoitettu jäykällä, vanhanaikaisella tyylillä, sillä se toimii suoraan laitteistolla, meidän on otettava huomioon, että sen kutsun on toimitettava tiukasti ja oikein muotoiltu pyyntö. Siksi, vaikka emme tarvitsisi joitakin muuttujia, meidän on silti täytettävä niiden koko käytöstä riippumatta. Tarkastellaan täysin toimivaa esimerkkiä.
Täysi 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 kattavasti nykyisen järjestelmän tiedot.
Voimme verrata tätä koodia lukkoon ja avaimeen.syscall.SYS_SYSINFO on avain, joka avaa lukon, joka sijaitsee ytimen sisällä.
Siksi oikean avaimen käyttäminen lukolle on tärkeää.
Mitä tapahtuu, kun käytämme tähän kutsuun syscall.SYS_GETPID-vakiota?
Tämä on avain lukolle, joka sisältää prosessitunnuksen.
Tämä yrittää hakea PID:n järjestelmätietojen tilasta.
Tämän seurauksena mitään tietoa ei voida lukea oikein, ja kutsun on palautettava epäonnistunut tila.
Nyt meidän on tiedettävä, mitkä kentät lokerossa ovat ja missä järjestyksessä ne ovat. Lokeron ensimmäisessä paikassa on Uptime, jonka koko on 2^64. Jos yritämme lukea tämän 2^32:n kokoisena, bittijono ei lue kokonaan. Emme voi käyttää tällaisia osittaisia binäärejä, ellemme aio kirjoittaa matalan tason temppuja.
Kun 64 bittiä binääridataa on luettu, olemme vihdoin toisessa paikassa. Sen voi lukea oikein vain, kun edellinen 64-bittinen kokonaisluku on luettu.
Toistamalla nämä tiukat ja loogiset vaiheet saamme järjestelmästä oikean tiedon, ja voimme käsitellä luettua dataa asianmukaisesti.
Kuinka ohittaa "muuttujanimet"
Vaikka itse muuttujia ei voi "ohittaa", on tärkeää erottaa käytetyt ja hylätyt muuttujat toisistaan. Jos ohjelman käyttötarkoitus on riittävän selkeä, on parempi käyttää nimettömiä muuttujia paikanvaraajina kuin nimetä jokainen arvo, vaikka niitä ei lopulta käytettäisi. Tarkastellaan tätä esimerkillä "Vapaan muistin tarkistin".
Esimerkki - Vapaan muistin tarkistin
Kun tarkistamme vapaata muistia tai vaihtotilaa, emme tarvitse muita tietoja, jotka viittaavat muihin resursseihin. Paremman selkeyden saavuttamiseksi voit luoda nimettömiä muuttujia pitämään tietyt tilat.
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 nimiä. Vaikka nimettömät arvot tallennetaan todellisuudessa rakenteeseen, koodissa ei ole nimiä tai luettavia merkintöjä.
Päätelmät
- Go-kielen
syscall- jaunsafe-paketin käyttö on edelleen turvallisempaa kuin C/CGo - Jos kirjoitat laajaa projektia, joka on helppo laajentaa:
- Älä käytä nimettömiä muuttujia; anna jokaiselle jäsenelle nimi.
- Jos kirjoitat projektia, jolla on rajallinen käyttö:
- Voit käyttää nimettömiä muuttujia pitämään tilaa tyhjille kentille, joita ei todellisuudessa käytetä.
- Go-kielen
syscallon tehokas ja moderni työkalu matalan tason kutsujen käsittelyyn