GoSuda

Go syscall, düşük seviyeli I/O için parlak bir alternatiftir.

By Yunjin Lee
views ...

Özet

Go üzerinde doğrudan sistem çağrılarını inceleyeceğiz. Go, katı derleyici hataları ve sıkı GC sunduğundan, düşük seviyeli çağrıları Pure Go ile değiştirmek çok daha iyidir. Neyse ki, C fonksiyon çağrılarının çoğu Go'da iyi ve çağdaş bir şekilde yeniden uygulanmıştır. Buna bir göz atalım.

Sistem Çağrısı

Sistem çağrısı, işletim sistemine yapılan doğrudan bir taleptir. Sistem genellikle donanım üzerinde çalıştığı için katı, eski moda bir tarzda yazıldığından, çağrısının katı ve doğru bir talep biçimi sunması gerektiğini göz önünde bulundurmamız gerekir. Bu nedenle, bazı değişkenlere ihtiyacımız olmasa bile, kullanıma bakılmaksızın boyutu yine de doldurmamız gerekir. Tamamen çalışan bir örnekle kontrol edelim.

Tam Örnek

 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	// syscall.SYS_SYSINFO sistem çağrısını yapar
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}

Bu örnek tüm değişkenleri içerir ve mevcut sistem bilgilerinin kapsamlı bir bilgisini yazdırır. Bu kodu bir kilit ve bir anahtar olarak karşılaştırabiliriz.syscall.SYS_SYSINFO, bir çekirdeğin içindeki bir kilidi açan bir anahtardır. Bu nedenle, bir kilit için doğru anahtarı kullanmak önemlidir. Bu çağrı için syscall.SYS_GETPID kullandığımızda ne olacak? Bu, Process ID'yi içeren bir kilit için bir anahtardır. Bu, sistem bilgisi için ayrılmış bir alandan bir PID almaya çalışacaktır. Sonuç olarak, bilgilerin hiçbiri doğru bir şekilde okunamayacaktır; çağrı başarısız bir durum olarak döndürülmelidir.

Şimdi, hangi öğelerin içerildiğini ve öğelerin nasıl sıralandığını bilmemiz gerekiyor. Bir kilidin ilk yuvasında, 2^64 boyutunda bir Uptime'ımız var. Bunu 2^32 ile okumaya çalışırsak, bit dizisi tam olarak okunmaz. Düşük seviyeli hileler yazmayacaksak, bu tür kısmi ikili dosyaları kullanamayız.

64 bitlik ikili veri okuduktan sonra, nihayet ikinci yuvadayız. Ancak önceki 64 bit boyutlu tamsayıyı okuduğumuzda doğru bir şekilde okunabilir.

Bir sistemden doğru bilgiyi elde etmek için bu katı ve mantıksal akışları tekrarlayarak, okunan verileri düzgün bir şekilde işleyebiliriz.

'Değişken adlarını' nasıl atlarız

Değişkenlerin kendilerini 'atlayamasak' da, kullanılan değişkenleri ve atılanları ayırt etmek önemlidir. Programın kullanımı yeterince netse, her bir değeri etiketlemek yerine isimsiz değişkenleri yer tutucu olarak kullanmak daha iyidir, hatta sonsuza dek kullanılmasalar bile. Bunu bir örnekle, "Boş Bellek Denetleyicisi" ile kontrol edelim.

Örnek - Boş Bellek Denetleyicisi

Boş Belleği/Swap'leri kontrol ederken, farklı kaynakları gösteren başka bilgilere ihtiyacımız yoktur. Daha iyi bir görünürlük elde etmek için, belirli alanları tutmak üzere anonim değişkenler oluşturabilirsiniz.

 1package main
 2
 3import (
 4	"fmt"
 5	"syscall"
 6	"unsafe"
 7)
 8
 9type sysinfo_t struct {
10	_          int64  // anonim ve kullanılmayanlar _ olarak işaretlenir
11	_         [3]uint64
12	Totalram  uint64
13	Freeram   uint64
14	Sharedram uint64
15	Bufferram uint64
16	Totalswap uint64
17	Freeswap  uint64
18	_         uint16  // anonim ve kullanılmayanlar _ olarak işaretlenir
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	// syscall.SYS_SYSINFO sistem çağrısını yapar
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}

Sonuç olarak, değişkenler etiketsiz olarak okunur. Anonim değerler aslında bir yapıya depolansa da, kod üzerinde etiketler/okunabilir işaretler bulunmamaktadır.

Sonuç

  • Go'nun syscall ve unsafe kullanımı hala C/CGo'dan daha güvenlidir.
  • Kolayca genişletilebilecek büyük bir proje yazıyorsanız:
    • Anonim değişkenler oluşturmayın; üyeler için her bir adı belirleyin.
  • Sınırlı kullanıma sahip bir proje yazıyorsanız:
    • Aslında kullanılmayan alanları tutmak için anonim değişkenler kullanabilirsiniz.
  • Go'nun syscall'ı düşük seviyeli çağrıları işlemek için güçlü ve moderndir.

Daha Fazla Oku

syscall unsafe x/sys/unix