GoSuda

Go syscall is een briljante vervanging van low-level I/O

By Yunjin Lee
views ...

Samenvatting

Wij zullen kennisnemen van directe systeemoproepen in Go. Aangezien Go strikte compilerfouten en rigide GC biedt, is het veel beter om low-level oproepen te vervangen door Puur Go. Gelukkig zijn de meeste C-functieaanroepen volledig opnieuw geïmplementeerd in Go, op een goede en eigentijdse manier. Laten wij hiernaar kijken.

Systeemoproep

Een systeemoproep is een direct verzoek aan het besturingssysteem. Aangezien het systeem doorgaans geschreven is in een rigide, ouderwetse stijl, omdat het rechtstreeks op de hardware draait, moeten wij in overweging nemen dat de oproep ervan een strikte en correcte vorm van een verzoek moet leveren. Dus, zelfs als wij bepaalde variabelen niet nodig hebben, moeten wij de omvang nog steeds invullen, ongeacht het gebruik. Laten wij dit controleren met een volledig werkend voorbeeld.

Volledig Voorbeeld

 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}

Dit voorbeeld omvat alle variabelen en drukt uitgebreide informatie over de huidige systeeminformatie af. Wij kunnen deze code vergelijken met een kluis en een sleutel.syscall.SYS_SYSINFO is een sleutel die een kluis binnenin de kernel ontgrendelt. Daarom is het belangrijk om de juiste sleutel voor een kluis te gebruiken. Wat zal er gebeuren als wij syscall.SYS_GETPID voor deze oproep gebruiken? Dit is een sleutel voor een kluis die de Proces-ID bevat. Dit zal proberen een PID te verkrijgen uit een ruimte voor systeeminformatie. Als gevolg hiervan kan geen van de informatie correct worden gelezen; de oproep moet worden teruggestuurd als een mislukte status.

Nu moeten wij weten welke items zijn opgenomen en hoe de items zijn gerangschikt. In het eerste slot van een kluis hebben wij Uptime, met een omvang van 2^64. Als wij dit proberen te lezen met 2^32, wordt de bitreeks niet volledig gelezen. Wij kunnen dit soort gedeeltelijke binaire bestanden niet gebruiken, tenzij wij low-level trucjes gaan schrijven.

Na het lezen van 64 bits binaire gegevens, zijn wij eindelijk bij het tweede slot. Dit kan alleen nauwkeurig worden gelezen wanneer wij de voorgaande 64-bit grote integer hebben gelezen.

Door deze strikte en logische stromen te herhalen om de juiste informatie uit een systeem te verkrijgen, kunnen wij gelezen gegevens correct verwerken.

Hoe 'variabelenamen' over te slaan

Hoewel wij de variabelen zelf niet kunnen 'overslaan', is het belangrijk om onderscheid te maken tussen gebruikte variabelen en weggegooide variabelen. Als het gebruik van het programma duidelijk genoeg is, is het beter om naamloze variabelen als plaatsaanduidingen te gebruiken dan elke waarde te labelen, zelfs als deze nooit worden gebruikt. Laten wij dit controleren met een voorbeeld, "Vrije Geheugen Controle".

Voorbeeld - Vrije Geheugen Controle

Bij het controleren van Vrij Geheugen/Swaps, hebben wij geen andere informatie nodig die verschillende bronnen aangeeft. Om een betere zichtbaarheid te bereiken, kunt u anonieme variabelen maken om specifieke ruimtes vast te houden.

 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 _ (anonieme en ongebruikte zijn gemarkeerd als _)
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}

Dientengevolge worden variabelen gelezen zonder labels. Hoewel anonieme waarden daadwerkelijk in een structuur worden opgeslagen, zijn er geen labels/leesbare markeringen op de code.

Conclusie

  • Het gebruik van Go's syscall en unsafe is nog steeds veiliger dan C/CGo
  • Als u een enorm project schrijft dat gemakkelijk kan worden uitgebreid:
    • Maak geen anonieme variabelen; maak namen voor elk van de leden.
  • Als u een project schrijft dat beperkt gebruik kent:
    • U kunt anonieme variabelen gebruiken om ruimtes vast te houden die feitelijk ongebruikt zijn.
  • Go's syscall is krachtig en modern om low-level oproepen af te handelen

Verder Lezen

syscall unsafe x/sys/unix