Go syscall является блестящей заменой низкоуровневого I/O
Summary
Мы узнаем о прямом системном вызове в Go. Поскольку Go предлагает строгие ошибки компилятора и жесткий GC, гораздо лучше заменить низкоуровневые вызовы на Pure Go. К счастью, большинство вызовов функций C полностью переписаны в Go, в хорошем и современном стиле. Давайте взглянем на это.
System Call (Системный Вызов)
Системный вызов — это прямой запрос к операционной системе. Поскольку система обычно написана в жестком, старомодном стиле, так как она работает непосредственно на аппаратном обеспечении, нам необходимо учитывать, что ее вызов должен предоставлять строгую и правильную форму запроса. Таким образом, даже если нам не нужны некоторые переменные, нам все равно необходимо заполнить размер независимо от использования. Давайте проверим это на полностью рабочем примере.
Full Example (Полный Пример)
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}
Этот пример включает все переменные и выводит обширную информацию о текущей системе.
Мы можем сравнить этот код с замком и ключом.syscall.SYS_SYSINFO — это ключ, который отпирает замок, находящийся внутри ядра.
Следовательно, важно использовать правильный ключ для замка.
Что произойдет, если мы используем syscall.SYS_GETPID для этого вызова?
Это ключ для замка, который содержит Process ID (Идентификатор Процесса).
Это приведет к попытке получить PID из пространства, предназначенного для системной информации.
В результате ни одна из частей информации не может быть прочитана корректно; вызов должен быть возвращен как состояние сбоя.
Теперь нам нужно знать, какие элементы содержатся и как они упорядочены. В первом слоте замка у нас есть Uptime (Время работы), размером 2^64. Если мы попытаемся прочитать это с помощью 2^32, битовая последовательность не будет прочитана полностью. Мы не можем использовать такие частичные бинарные данные, если только мы не собираемся писать низкоуровневые трюки.
После прочтения 64 бит бинарных данных, мы, наконец, переходим ко второму слоту. Его можно прочитать точно только тогда, когда мы прочитали предыдущее 64-битное целое число.
Повторяя эти строгие и логические потоки для получения надлежащей информации из системы, мы можем правильно обрабатывать прочитанные данные.
How to skip 'variable names' (Как пропустить «имена переменных»)
Хотя мы не можем «пропустить» сами переменные, важно различать используемые переменные и отброшенные. Если назначение программы достаточно ясно, лучше использовать безымянные переменные в качестве заполнителей, чем маркировать каждое значение, даже если оно никогда не будет использоваться. Давайте проверим это на примере «Free Memory Checker» (Проверка Свободной Памяти).
Example - Free Memory Checker (Пример - Проверка Свободной Памяти)
При проверке Свободной Памяти/Свопов нам не нужна другая информация, указывающая на другие ресурсы. Для достижения лучшей наглядности вы можете сделать анонимные переменные для хранения определенных пространств.
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}
Следовательно, переменные считываются без меток. Хотя анонимные значения фактически сохраняются в структуре, в коде нет меток/различимых отметок.
Conclusion (Заключение)
- Использование
syscallиunsafeв Go по-прежнему безопаснее, чем в C/CGo - Если вы пишете огромный проект, который можно легко расширить:
- Не создавайте анонимные переменные; создайте отдельные имена для членов.
- Если вы пишете проект с ограниченным использованием:
- Вы можете использовать анонимные переменные для хранения пространств, которые фактически не используются.
syscallв Go является мощным и современным средством для обработки низкоуровневых вызовов