GoSuda

Go syscall, düşük seviyeli I/O'nun parlak bir ikamesidir.

By Yunjin Lee
views ...

Özet

Go'da doğrudan sistem çağrısı hakkında bilgi edineceğiz. Go, katı compiler hataları ve kesin GC (Garbage Collection) sunduğu için, düşük seviyeli çağrıları Saf Go'da 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 tamamen yeniden uygulanmıştır. Buna bir göz atalım.

Sistem Çağrısı

Sistem çağrısı, işletim sistemine doğrudan bir taleptir. Sistem genellikle katı, eski moda bir tarzda yazıldığı ve donanım üzerinde doğrudan çalıştığı için, çağrısının kesin ve doğru bir talep biçimini iletmesi gerektiğini göz önünde bulundurmalıyız. Bu nedenle, bazı değişkenlere ihtiyacımız olmasa bile, kullanımdan bağımsız olarak 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	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
29	if errno != 0 {
30		fmt.Println("sysinfo syscall failed:", errno) // sysinfo sistem çağrısı başarısız oldu:
31		return
32	}
33
34	scale := float64(1 << 16)
35	fmt.Printf("Uptime: %d seconds\n", info.Uptime) // Çalışma Süresi: %d saniye
36	fmt.Printf("Load Average: %.2f %.2f %.2f\n", // Yük Ortalaması:
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", // Bellek: toplam=%d MB boş=%d MB tampon=%d MB
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", // Takas: toplam=%d MB boş=%d MB
45		info.Totalswap*uint64(info.MemUnit)/1024/1024,
46		info.Freeswap*uint64(info.MemUnit)/1024/1024)
47	fmt.Printf("Processes: %d\n", info.Procs) // İşlemler: %d
48}

Bu örnek, tüm değişkenleri içerir ve mevcut sistem bilgisine dair kapsamlı bilgi yazdırır. Bu kodu bir kilitli dolap ve anahtar olarak karşılaştırabiliriz.syscall.SYS_SYSINFO, bir kernel'in içindeki kilitli dolabın kilidini açan bir anahtardır. Bu nedenle, bir kilitli dolap için doğru anahtarı kullanmak önemlidir. Bu çağrı için syscall.SYS_GETPID kullanırsak ne olur? Bu, Process ID'yi (İşlem Kimliği) içeren bir kilitli dolap için bir anahtardır. Bu, sistem bilgisi için ayrılan 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 durum olarak geri dönmelidir.

Şimdi, hangi öğelerin bulunduğunu ve öğelerin nasıl sıralandığını bilmemiz gerekiyor. Kilitli dolabın ilk yuvasında, 2^64 boyutunda Uptime bulunur. Bunu 2^32 ile okumaya çalışırsak, bit dizisi tam olarak okunmaz. Düşük seviyeli hileler yazmayacaksak, bu tür kısmi ikilikleri kullanamayız.

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

Sistemden doğru bir bilgi elde etmek için bu katı ve mantıksal akışları tekrarlayarak, okunan veriyi 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ılan değişkenleri ayırt etmek önemlidir. Programın kullanımı yeterince açıksa, her değeri etiketlemek yerine, sonsuza kadar kullanılmayacak olsalar bile, yer tutucu olarak isimsiz değişkenler kullanmak daha iyidir. Bunu bir örnekle kontrol edelim, "Boş Bellek Denetleyicisi"

Örnek - Boş Bellek Denetleyicisi

Boş Bellek/Takasları kontrol ederken, farklı kaynakları gösteren diğer bilgilere ihtiyacımız yoktur. Daha iyi bir görünürlük elde etmek için, belirli boşlukları 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  
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) // sysinfo sistem çağrısı başarısız oldu:
32		return
33	}
34
35	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n", // Bellek: toplam=%d MB boş=%d MB tampon=%d MB
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", // Takas: toplam=%d MB boş=%d MB
40		info.Totalswap*uint64(info.MemUnit)/1024/1024,
41		info.Freeswap*uint64(info.MemUnit)/1024/1024)
42}

Sonuç olarak, değişkenler etiketsiz olarak okunur. Anonim değerler aslında bir structure içine depolansa da, kod üzerinde etiketler/okunabilir işaretler yoktur.

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 birine ad verin.
  • Sınırlı kullanıma sahip bir proje yazıyorsanız:
    • Gerçekte kullanılmayan boşlukları tutmak için anonim değişkenler kullanabilirsiniz.
  • Go'nun syscall'u, düşük seviyeli çağrıları ele almak için güçlü ve moderndir

Daha Fazla Oku

syscall unsafe x/sys/unix