Go syscall е превъзходен заместител на нисконисковото I/O
Резюме
Ще научим за директни системни извиквания в Go. Тъй като Go предлага строги грешки на компилатора и стриктен GC, много по-добре е да се заменят нисконивови извиквания в чист Go. За щастие, повечето от C извикванията на функции са изцяло преимплементирани в Go, по добър и съвременен начин. Нека да го разгледаме.
Системно извикване
Системното извикване е директна заявка към операционната система. Тъй като системата обикновено е написана в строг, старомоден стил, тъй като работи директно върху хардуера, трябва да имаме предвид, че нейното извикване трябва да осигурява строга и коректна форма на заявка. Така че, дори ако не ни трябват някои променливи, все пак трябва да попълним размера, независимо от употребата. Нека проверим с напълно работещ пример.
Пълен пример
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 // Извиква системната функция SYS_SYSINFO с указател към структурата info като първи аргумент.
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}
Този пример включва всички променливи и отпечатва изчерпателна информация за текущата системна информация.
Можем да сравним този код с шкафче и ключ.syscall.SYS_SYSINFO е ключ, който отключва шкафче, което се намира в ядрото.
Следователно, използването на правилния ключ за шкафче е важно.
Какво ще се случи, ако използваме syscall.SYS_GETPID за това извикване?
Това е ключ за шкафче, което съдържа ID на процес.
Това ще се опита да получи PID от пространство за системна информация.
В резултат на това, никоя от информациите не може да бъде прочетена правилно; извикването трябва да бъде върнато като неуспешно състояние.
Сега трябва да знаем кои елементи се съдържат и как са подредени елементите. В първия слот на шкафчето имаме Uptime, с размер 2^64. Ако се опитаме да прочетем това с 2^32, битовата последователност не е напълно прочетена. Не можем да използваме такива частични двоични файлове, освен ако няма да пишем нисконивови трикове.
След прочитане на 64 бита двоични данни, най-накрая сме на втория слот. Той може да бъде прочетен точно само когато сме прочели предишното 64-битово цяло число.
С повтарянето на тези строги и логически потоци за получаване на правилна информация от система, можем правилно да обработваме прочетените данни.
Как да пропуснем „имена на променливи“
Въпреки че не можем да „пропуснем“ самите променливи, важно е да се разграничават използваните променливи и изхвърлените. Ако употребата на програмата е достатъчно ясна, по-добре е да се използват безименни променливи като заместители, отколкото да се етикетират всяка стойност, дори ако те не се използват завинаги. Нека проверим това с пример, „Проверка на свободната памет“
Пример – Проверка на свободната памет
При проверка на свободната памет/суап, не ни е необходима друга информация, която показва различни ресурси. За да постигнете по-добра видимост, можете да направите анонимни променливи, които да държат специфични пространства.
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 // анонимните и неизползвани са маркирани като _
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 // Извиква системната функция SYS_SYSINFO с указател към структурата info като първи аргумент.
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}
Вследствие на това променливите се четат без етикети. Въпреки че анонимните стойности всъщност се съхраняват в структура, няма етикети/четими марки в кода.
Заключение
- Използването на
syscallиunsafeна Go все още е по-безопасно от C/CGo - Ако пишете огромен проект, който може лесно да бъде разширен:
- Не правете анонимни променливи; направете имена за всеки член.
- Ако пишете проект с ограничена употреба:
- Можете да използвате анонимни променливи, за да държите пространства, които всъщност не се използват.
syscallна Go е мощен и модерен за обработка на нисконивови извиквания