GoSuda

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

By Yunjin Lee views ...

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- ja unsafe-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 syscall on tehokas ja moderni työkalu matalan tason kutsujen käsittelyyn

Lue lisää

syscall unsafe x/sys/unix