GoSuda

Panggilan Sistem Linux Langsung dalam Go Murni dengan syscall.Syscall dan unsafe.Pointer

By Yunjin Lee views ...

Ringkasan

Kita akan mempelajari system call langsung di Go. Karena Go menawarkan pesan kesalahan compiler yang ketat dan GC yang kaku, lebih baik mengganti panggilan tingkat rendah dengan Pure Go. Untungnya, sebagian besar pemanggilan fungsi C telah diimplementasikan ulang sepenuhnya di Go, dengan cara yang baik dan modern. Mari kita lihat contohnya.

System Call

System call adalah permintaan langsung ke sistem operasi. Karena sistem biasanya ditulis dengan gaya yang kaku dan kuno karena berjalan langsung di atas perangkat keras, kita perlu mempertimbangkan bahwa pemanggilannya harus menyampaikan bentuk permintaan yang ketat dan benar. Oleh karena itu, meskipun kita tidak memerlukan beberapa variabel, kita tetap harus mengisi ukurannya terlepas dari penggunaannya. Mari kita periksa dengan contoh yang berfungsi penuh.

Contoh Lengkap

 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}

Contoh ini menyertakan semua variabel, dan mencetak informasi sistem terkini secara lengkap. Kita dapat membandingkan kode ini dengan gembok dan kunci.syscall.SYS_SYSINFO adalah kunci yang membuka gembok yang berada di dalam kernel. Oleh karena itu, menggunakan kunci yang tepat untuk gembok itu penting. Apa yang akan terjadi jika kita menggunakan syscall.SYS_GETPID untuk pemanggilan ini? Ini adalah kunci untuk gembok yang berisi Process ID. Ini akan mencoba mengambil PID dari ruang informasi sistem. Akibatnya, tidak ada informasi yang dapat dibaca dengan benar; pemanggilan tersebut harus dikembalikan dalam keadaan gagal.

Sekarang, kita perlu mengetahui item apa saja yang terdapat di dalamnya, dan bagaimana item-item tersebut diurutkan. Pada slot pertama gembok, terdapat Uptime, dengan ukuran 2^64. Jika kita mencoba membacanya dengan 2^32, urutan bit tidak terbaca secara utuh. Kita tidak dapat menggunakan potongan biner parsial seperti ini kecuali kita akan menulis trik tingkat rendah.

Setelah membaca 64 bit data biner, barulah kita sampai pada slot kedua. Slot ini hanya dapat dibaca dengan akurat ketika kita sudah membaca bilangan bulat 64-bit sebelumnya.

Dengan mengulangi alur yang ketat dan logis ini untuk memperoleh informasi yang tepat dari sistem, kita dapat menangani data yang dibaca dengan benar.

Cara Melewati 'Nama Variabel'

Meskipun kita tidak dapat 'melewati' variabel itu sendiri, penting untuk membedakan antara variabel yang digunakan dan yang dibuang. Jika penggunaan program sudah cukup jelas, lebih baik menggunakan variabel tanpa nama sebagai penampung daripada memberi label pada setiap nilai, meskipun nilai tersebut tidak akan digunakan selamanya. Mari kita periksa hal ini dengan sebuah contoh, "Free Memory Checker"

Contoh - Free Memory Checker

Saat memeriksa Free Memory/Swap, kita tidak memerlukan informasi lain yang menunjukkan sumber daya berbeda. Untuk mencapai visibilitas yang lebih baik, Anda dapat membuat variabel anonim untuk menampung ruang tertentu.

 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  // anonim, dan yang tidak digunakan ditandai sebagai _
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}

Akibatnya, variabel dibaca tanpa label. Meskipun nilai anonim sebenarnya disimpan ke dalam sebuah struktur, tidak ada label atau tanda yang dapat dibaca pada kode.

Kesimpulan

  • Menggunakan syscall dan unsafe di Go tetap lebih aman daripada C/CGo
  • Jika Anda menulis proyek besar yang dapat diperluas dengan mudah:
    • Jangan membuat variabel anonim; berikan nama untuk setiap anggota.
  • Jika Anda menulis proyek dengan penggunaan terbatas:
    • Anda dapat menggunakan variabel anonim untuk menampung ruang yang memang tidak digunakan.
  • syscall di Go powerful dan modern untuk menangani panggilan tingkat rendah

Bacaan Lanjutan

syscall unsafe x/sys/unix