Go syscall este un înlocuitor strălucit al I/O de nivel scăzut
Rezumat
Vom învăța despre apelul de sistem direct pe Go. Deoarece Go oferă erori stricte ale compilatorului și GC rigid, este mult mai bine să înlocuim apelurile de nivel scăzut în Pure Go. Din fericire, majoritatea apelurilor de funcții C sunt complet re implementate în Go, într-o manieră bună și contemporană. Să aruncăm o privire asupra acestui 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, demodat, întrucât rulează direct pe hardware, trebuie să luăm în considerare că apelul său trebuie să livreze o formă strictă și corectă a unei solicitări. Așadar, chiar dacă nu avem nevoie de unele 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ă apelul de sistem SYS_SYSINFO
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 scale := float64(1 << 16)
36 fmt.Printf("Uptime: %d seconds\n", info.Uptime)
37 fmt.Printf("Load Average: %.2f %.2f %.2f\n",
38 float64(info.Loads[0])/scale,
39 float64(info.Loads[1])/scale,
40 float64(info.Loads[2])/scale)
41 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
42 info.Totalram*uint64(info.MemUnit)/1024/1024,
43 info.Freeram*uint64(info.MemUnit)/1024/1024,
44 info.Bufferram*uint64(info.MemUnit)/1024/1024)
45 fmt.Printf("Swap: total=%d MB free=%d MB\n",
46 info.Totalswap*uint64(info.MemUnit)/1024/1024,
47 info.Freeswap*uint64(info.MemUnit)/1024/1024)
48 fmt.Printf("Processes: %d\n", info.Procs)
49}
Acest exemplu include toate variabilele și afișează informații extinse despre informațiile curente ale sistemului.
Putem compara acest cod cu o încuietoare și o cheie.syscall.SYS_SYSINFO este o cheie care deblochează o încuietoare care se află în interiorul unui kernel.
Prin urmare, utilizarea cheii corecte pentru o încuietoare este importantă.
Ce se va întâmpla dacă folosim syscall.SYS_GETPID pentru acest apel?
Aceasta este o cheie pentru o încuietoare care conține ID-ul Procesului (Process ID).
Aceasta va încerca să obțină un PID dintr-un spațiu destinat informațiilor de sistem.
Drept urmare, niciuna dintre informații nu poate fi citită corect; apelul trebuie returnat ca stare eșuată.
Acum, trebuie să știm ce elemente sunt conținute și cum sunt ordonate elementele. În primul slot al unei încuietori, avem Uptime, cu o dimensiune de 2^64. Dacă încercăm să citim acest lucru cu 2^32, secvența de biți nu este citită complet. Nu putem folosi acest gen de binari parțiali decât dacă intenționăm să scriem trucuri de nivel scăzut.
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 atunci când am citit anterior un întreg de 64 de biți.
Prin repetarea acestor fluxuri stricte și logice pentru a obține informații adecvate de la un sistem, putem gestiona corect datele citite.
Cum să omitem 'numele variabilelor'
Deși nu putem 'omite' variabilele în sine, este important să distingem variabilele folosite de cele aruncate. Dacă utilizarea programului este suficient de clară, este mai bine să folosim variabile fără nume ca substituenți (placeholders) decât să etichetăm fiecare valoare, chiar dacă acestea nu sunt folosite niciodată. Să verificăm acest lucru cu un exemplu, "Free Memory Checker" (Verificator de Memorie Liberă).
Exemplu - Verificator de Memorie Liberă
Atunci când verificăm Memoria/Swap-urile Libere, 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 păstra spații specifice.
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 // cele anonime și neutilizate sunt marcate ca _
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 // Efectuează apelul de sistem SYS_SYSINFO
30 _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
31 if errno != 0 {
32 fmt.Println("sysinfo syscall failed:", errno)
33 return
34 }
35
36 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
37 info.Totalram*uint64(info.MemUnit)/1024/1024,
38 info.Freeram*uint64(info.MemUnit)/1024/1024,
39 info.Bufferram*uint64(info.MemUnit)/1024/1024)
40 fmt.Printf("Swap: total=%d MB free=%d MB\n",
41 info.Totalswap*uint64(info.MemUnit)/1024/1024,
42 info.Freeswap*uint64(info.MemUnit)/1024/1024)
43}
Î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 încă mai sigură decât C/CGo - Dacă scrieți un proiect vast care poate fi extins cu ușurință:
- Nu creați variabile anonime; creați nume pentru fiecare membru.
- Dacă scrieți un proiect cu utilizare limitată:
- Puteți folosi variabile anonime pentru a păstra spații care de fapt nu sunt utilizate.
syscall-ul din Go este puternic și modern pentru a gestiona apelurile de nivel scăzut