---
id: c90263519492728e7cc2d0ce840057b6
author: Yunjin Lee
title: Pure Go ile syscall.Syscall ve unsafe.Pointer Kullanarak Doğrudan Linux Sistem Çağrıları
description: Saf Go ile, ABI'ye hizalanmış yapılar (struct), unsafe.Pointer ve standart syscall paketi kullanılarak, cgo olmadan SYS_SYSINFO gibi Linux çekirdek sistem çağrılarının doğrudan çağrılması.
language: tr
date: 2025-10-26T12:31:21.655228078Z
path: /blog/posts/go-syscall-is-a-brilliant-replacement-of-low-level-i-o-za50951e1
category: lowlevel
---

## Özet

Go üzerinde doğrudan sistem çağrılarını (direct system call) öğreneceğiz.
Go katı derleyici hataları ve sıkı bir çöp toplayıcı (GC) sunduğu için, düşük seviyeli çağrıları Saf 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 biçimde yeniden uygulanmıştır.
Bir göz atalım.

## Sistem Çağrısı

Sistem çağrısı, işletim sistemine yapılan doğrudan bir istektir.
Sistem genellikle donanımın üzerinde doğrudan çalıştığı için katı ve eski usul bir biçimde yazıldığından, çağrısının katı ve doğru biçimde bir istek iletmesi gerektiğini göz önünde bulundurmalıyız.
Bu nedenle, bazı değişkenlere ihtiyacımız olmasa bile, kullanılsın veya kullanılmasın boyutunu yine de doldurmamız gerekir.
Tamamen çalışan bir örneğe bakalım.

## Tam Örnek

```go
package main
import (
	"fmt"
	"syscall"
	"unsafe"
)

type sysinfo_t struct {
	Uptime    int64
	Loads     [3]uint64
	Totalram  uint64
	Freeram   uint64
	Sharedram uint64
	Bufferram uint64
	Totalswap uint64
	Freeswap  uint64
	Procs     uint16
	Pad       uint16
	_         [4]byte
	Totalhigh uint64
	Freehigh  uint64
	MemUnit   uint32
	_         [4]byte
}

func main() {
	var info sysinfo_t
	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
	if errno != 0 {
		fmt.Println("sysinfo syscall failed:", errno)
		return
	}

	scale := float64(1 << 16)
	fmt.Printf("Uptime: %d seconds\n", info.Uptime)
	fmt.Printf("Load Average: %.2f %.2f %.2f\n",
		float64(info.Loads[0])/scale,
		float64(info.Loads[1])/scale,
		float64(info.Loads[2])/scale)
	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
		info.Totalram*uint64(info.MemUnit)/1024/1024,
		info.Freeram*uint64(info.MemUnit)/1024/1024,
		info.Bufferram*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Swap: total=%d MB free=%d MB\n",
		info.Totalswap*uint64(info.MemUnit)/1024/1024,
		info.Freeswap*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Processes: %d\n", info.Procs)
}
```

Bu örnek tüm değişkenleri içermekte ve mevcut sistem bilgisinin kapsamlı bir dökümünü yazdırmaktadır.
Bu kodu bir kilit ve anahtar gibi karşılaştırabiliriz.
`syscall.SYS_SYSINFO`, bir çekirdeğin içindeki kilidi açan bir anahtardır.
Bu nedenle, kilit için doğru anahtarı kullanmak önemlidir.
Bu çağrı için `syscall.SYS_GETPID` kullanırsak ne olur?
Bu, İşlem Kimliğini (Process ID) içeren bir kilit için bir anahtardır.
Bu, sistem bilgisi alanından bir PID almaya çalışacaktır.
Sonuç olarak, bilgilerin hiçbiri doğru biçimde okunamaz; çağrı başarısız durumla geri dönmelidir.

Şimdi, hangi öğelerin bulunduğunu ve öğelerin nasıl sıralandığını bilmemiz gerekir.
Kilidin ilk yuvasında, 2^64 boyutunda Uptime bulunur.
Bunu 2^32 ile okumaya çalışırsak, bit dizisi tam olarak okunmamış olur.
Düşük seviyeli numaralar yazmadığımız sürece bu tür kısmi ikili verileri kullanamayız.

64 bitlik ikili veriyi okuduktan sonra, sonunda ikinci yuvadayız.
Bu yuva, yalnızca önceki 64 bitlik tam sayı okunduktan sonra doğru biçimde okunabilir.

Bu katı ve mantıksal akışları tekrarlayarak, sistemden doğru bilgiyi elde edebilir ve okunan verileri düzgün biçimde işleyebiliriz.

## Değişken adlarını nasıl atlayabiliriz

Değişkenlerin kendilerini "atlayamasak" da, kullanılan ve atılan değişkenleri ayırt etmek önemlidir.
Programın kullanımı yeterince açıksa, değerleri kalıcı olarak kullanılmasa bile her birini etiketlemek yerine, yer tutucu olarak adsız değişkenler kullanmak daha iyidir.
Bunu "Boş Bellek Denetleyicisi" adlı bir örnekle inceleyelim.

## Örnek - Boş Bellek Denetleyicisi

Boş Bellek/Takas alanını 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 alanları tutmak üzere adsız değişkenler oluşturabilirsiniz.

```go
package main

import (
	"fmt"
	"syscall"
	"unsafe"
)

type sysinfo_t struct {
	_          int64
	_         [3]uint64
	Totalram  uint64
	Freeram   uint64
	Sharedram uint64
	Bufferram uint64
	Totalswap uint64
	Freeswap  uint64
	_         uint16  // anonim olan ve kullanılmayanlar _ ile işaretlenir
	_         uint16
	_         [4]byte
	_         uint64
	_         uint64
	MemUnit   uint32
	_         [4]byte
}

func main() {
	var info sysinfo_t
	_, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
	if errno != 0 {
		fmt.Println("sysinfo syscall failed:", errno)
		return
	}

	fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
		info.Totalram*uint64(info.MemUnit)/1024/1024,
		info.Freeram*uint64(info.MemUnit)/1024/1024,
		info.Bufferram*uint64(info.MemUnit)/1024/1024)
	fmt.Printf("Swap: total=%d MB free=%d MB\n",
		info.Totalswap*uint64(info.MemUnit)/1024/1024,
		info.Freeswap*uint64(info.MemUnit)/1024/1024)
}
```

Sonuç olarak, değişkenler etiketsiz okunur.
Adsız değerler aslında bir yapıda saklansa da, kodda okunabilir etiketler/işaretler bulunmaz.

## Sonuç

- Go'nun `syscall` ve `unsafe` paketlerini kullanmak, C/CGo'ya göre hâlâ daha güvenlidir
- Kolayca genişletilebilecek büyük bir proje yazıyorsanız:
  - Adsız değişkenler kullanmayın; üyeler için her birine ad verin.
- Sınırlı kullanıma sahip bir proje yazıyorsanız:
  - Gerçekte kullanılmayan alanları tutmak için adsız değişkenleri kullanabilirsiniz.
- Go'nun `syscall` paketi, düşük seviyeli çağrıları işlemek için güçlü ve modern bir araçtır

## Daha Fazla Okuma

[syscall](https://pkg.go.dev/syscall)
[unsafe](https://pkg.go.dev/unsafe)
[x/sys/unix](https://pkg.go.dev/golang.org/x/sys/unix)
