Go syscall este un înlocuitor genial pentru I/O de nivel scăzut
Rezumat
Vom studia apelurile de sistem directe în Go. Deoarece Go oferă erori stricte de compilare și un GC rigid, este mult mai bine să înlocuim apelurile de nivel jos în Pure Go. Din fericire, majoritatea apelurilor de funcții C sunt reimplementate integral în Go, într-o manieră bună și contemporană. Să analizăm acest aspect.
Apel de sistem
Apelul de sistem este o solicitare directă către sistemul de operare. Deoarece sistemul este de obicei scris într-un stil rigid, vechi, fiindcă rulează direct pe hardware, trebuie să considerăm că apelul său trebuie să livreze o formă strictă și corectă a unei solicitări. Așadar, chiar dacă nu avem nevoie de anumite variabile, tot trebuie să completăm dimensiunea, indiferent de utilizare. Să verificăm cu un exemplu complet funcțional.
Exemplu complet
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 // Efectuează un apel de sistem SYS_SYSINFO pentru a obține informații despre sistem
29 _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
30 // Verifică dacă apelul de sistem a eșuat
31 if errno != 0 {
32 fmt.Println("sysinfo syscall failed:", errno)
33 return
34 }
35
36 scale := float64(1 << 16)
37 fmt.Printf("Uptime: %d seconds\n", info.Uptime)
38 fmt.Printf("Load Average: %.2f %.2f %.2f\n",
39 float64(info.Loads[0])/scale,
40 float64(info.Loads[1])/scale,
41 float64(info.Loads[2])/scale)
42 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
43 info.Totalram*uint64(info.MemUnit)/1024/1024,
44 info.Freeram*uint64(info.MemUnit)/1024/1024,
45 info.Bufferram*uint64(info.MemUnit)/1024/1024)
46 fmt.Printf("Swap: total=%d MB free=%d MB\n",
47 info.Totalswap*uint64(info.MemUnit)/1024/1024,
48 info.Freeswap*uint64(info.MemUnit)/1024/1024)
49 fmt.Printf("Processes: %d\n", info.Procs)
50}
Acest exemplu include toate variabilele și afișează informații extinse despre sistemul curent.
Putem compara acest cod cu un dulap și o cheie.syscall.SYS_SYSINFO este o cheie care deblochează un dulap aflat în interiorul unui kernel.
Prin urmare, utilizarea cheii corecte pentru un dulap este importantă.
Ce se va întâmpla dacă vom folosi syscall.SYS_GETPID pentru acest apel?
Aceasta este o cheie pentru un dulap care conține ID-ul procesului.
Aceasta va încerca să obțină un PID dintr-un spațiu destinat informațiilor de sistem.
Ca rezultat, nicio informație nu poate fi citită corect; apelul trebuie să returneze o stare de eșec.
Acum, trebuie să știm ce elemente sunt conținute și cum sunt ordonate elementele. În primul slot al unui dulap, avem Uptime, cu o dimensiune de 2^64. Dacă încercăm să citim aceasta cu 2^32, secvența de biți nu este citită complet. Nu putem folosi aceste tipuri de binare parțiale decât dacă vom scrie trucuri de nivel jos.
După citirea a 64 de biți de date binare, în sfârșit suntem pe al doilea slot. Acesta poate fi citit cu precizie doar după ce am citit anterior un întreg de 64 de biți.
Prin repetarea acestor fluxuri stricte și logice pentru a obține informații corecte de la un sistem, putem gestiona corect datele citite.
Cum să omiteți „numele variabilelor”
Chiar dacă nu putem „omite” variabilele în sine, este important să distingem variabilele utilizate de cele eliminate. Dacă utilizarea programului este suficient de clară, este mai bine să folosiți variabile fără nume ca substituenți decât să etichetați fiecare valoare chiar dacă acestea nu sunt utilizate niciodată. Să verificăm acest lucru cu un exemplu, „Verificator de Memorie Liberă”.
Exemplu - Verificator de Memorie Liberă
Atunci când verificăm Memoria Liberă/Swap, nu avem nevoie de alte informații care indică resurse diferite. Pentru a obține o vizibilitate mai bună, puteți crea variabile anonime pentru a ocupa spații specifice.
1package main
2
3import (
4 "fmt"
5 "syscall"
6 "unsafe"
7)
8
9type sysinfo_t struct {
10 _ int64 // Câmp anonim, neutilizat
11 _ [3]uint64 // Câmp anonim, neutilizat
12 Totalram uint64
13 Freeram uint64
14 Sharedram uint64
15 Bufferram uint64
16 Totalswap uint64
17 Freeswap uint64
18 _ uint16 // Câmp anonim, neutilizat
19 _ uint16 // Câmp anonim, neutilizat
20 _ [4]byte // Câmp anonim, neutilizat
21 _ uint64 // Câmp anonim, neutilizat
22 _ uint64 // Câmp anonim, neutilizat
23 MemUnit uint32
24 _ [4]byte // Câmp anonim, neutilizat
25}
26
27func main() {
28 var info sysinfo_t
29 // Efectuează un apel de sistem SYS_SYSINFO pentru a obține informații despre sistem
30 _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
31 // Verifică dacă apelul de sistem a eșuat
32 if errno != 0 {
33 fmt.Println("sysinfo syscall failed:", errno)
34 return
35 }
36
37 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
38 info.Totalram*uint64(info.MemUnit)/1024/1024,
39 info.Freeram*uint64(info.MemUnit)/1024/1024,
40 info.Bufferram*uint64(info.MemUnit)/1024/1024)
41 fmt.Printf("Swap: total=%d MB free=%d MB\n",
42 info.Totalswap*uint64(info.MemUnit)/1024/1024,
43 info.Freeswap*uint64(info.MemUnit)/1024/1024)
44}
În consecință, variabilele sunt citite fără etichete. Deși valorile anonime sunt de fapt stocate într-o structură, nu există etichete/semne lizibile în cod.
Concluzie
- Utilizarea
syscallșiunsafedin Go este în continuare mai sigură decât C/CGo - Dacă scrieți un proiect amplu care poate fi extins cu ușurință:
- Nu creați variabile anonime; denumiți fiecare membru.
- Dacă scrieți un proiect cu utilizare limitată:
- Puteți utiliza variabile anonime pentru a ocupa spații care sunt de fapt neutilizate.
syscalldin Go este puternic și modern pentru a gestiona apelurile de nivel jos