Ders 16 / 16
Bellek Düzeni — Yığıt ve Öbek
Bir sürecin adres uzayı bölmeleri, çağrı yığıtının işleyişi, öbek üzerinde ayırma ve yaşam süresi kuralları.
İçindekiler
Yükleyici, çalıştırılabilir dosyanın bölümlerini sürecin adres uzayına yerleştirip denetimi programa devretmişti. Bu ders, o adres uzayının içini açar: hangi veri nerede durur ve ne kadar süre yaşar?
Soru pratiktir. Bir fonksiyonun yerel değişkeninin, fonksiyon döndükten sonra neden geçersiz olduğu; sonsuz özyinelemenin neden çöküşle bittiği; ayrılan belleğin neden serbest bırakılması gerektiği — hepsi bu düzenin sonuçlarıdır.
Adres Uzayının Bölmeleri
Bir sürecin adres uzayı, farklı davranışlara sahip bölmelere ayrılır:
yüksek adresler ┌──────────────────────────┐ │ Yığıt (stack) │ ↓ aşağı doğru büyür ├──────────────────────────┤ │ ... │ boş alan ├──────────────────────────┤ │ Öbek (heap) │ ↑ yukarı doğru büyür ├──────────────────────────┤ │ Başlatılmamış veri │ sıfırlanmış küresel değişkenler ├──────────────────────────┤ │ Başlatılmış veri │ değer verilmiş küresel değişkenler ├──────────────────────────┤ │ Salt okunur veri │ sabitler ├──────────────────────────┤ │ Kod │ komutlar; yazmaya kapalı └──────────────────────────┘ düşük adresler
Yığıt ile öbeğin karşılıklı uçlardan büyümesi, aradaki boş alanı ikisinin ortak kullanmasını sağlar: hangisi daha çok yer isterse ona doğru genişler.
Kod bölmesinin yazmaya kapalı olması bir güvenlik önlemidir: çalışan programın kendi komutlarını değiştirmesi engellenir. Aynı biçimde, veri bölmelerinin çalıştırmaya kapalı olması, veri olarak yerleştirilmiş baytların komut olarak yürütülmesini engeller — bu ayrım, bellek güvenliği açıklarına karşı temel savunmalardan biridir.
Çağrı Yığıtı
Bir fonksiyon çağrıldığında, yığıtın tepesine o çağrıya ait bir çerçeve (stack frame) eklenir. Çerçeve tipik olarak şunları taşır:
- Dönüş adresi: Fonksiyon bittiğinde akışın süreceği komutun adresi.
- Argümanlar: Yazmaçlara sığmayanlar.
- Yerel değişkenler: Fonksiyonun kendi değişkenleri.
- Saklanan yazmaçlar: Çağıran tarafın korunması gereken yazmaç içerikleri.
Bir yazmaç — yığıt işaretçisi — tepenin adresini tutar. Çağrıda tepe aşağı iner, dönüşte yukarı çıkar. Kaydırılan tek şey bir yazmaç değeri olduğundan çerçeve ayırmak ve bırakmak neredeyse ücretsizdir.
Bu düzenin yığıt yapısına uyması bir tesadüf değildir: çağrılar iç içedir. En son çağrılan fonksiyon, en önce döner. Komutlar dersinde tanımlanan çağrı ve dönüş komutlarının dönüş adresini nereye sakladığı sorusunun yanıtı budur.
Yerel değişkenlerin fonksiyon dönünce geçersizleşmesinin nedeni de görünür: çerçeve bırakılmıştır ve o alan bir sonraki çağrıda kullanılacaktır. Yerel bir değişkenin adresini döndürmek, kısa süre sonra başka bir çerçevenin işgal edeceği bir yeri göstermek demektir.
Yığıt Taşması
Yığıt sonsuz değildir; iş parçacığı başına sabit bir üst sınırla oluşturulur. Sınır iki biçimde aşılır: çok derin çağrı zinciri veya çerçeve başına çok büyük yerel veri.
Taban durumu olmayan özyineleme, birinci biçimin kanonik örneğidir. Her çağrı bir çerçeve ekler, hiçbiri dönmez ve yığıt tükenir.
import sys print(sys.getrecursionlimit()) # çalışma zamanının koyduğu sınır def derinlik(n: int = 1) -> int: """Sınıra ulaşana kadar kendini çağırır ve ulaşılan derinliği döndürür.""" try: return derinlik(n + 1) except RecursionError: return n print(derinlik()) # sınıra yakın bir sayı
Buradaki sınır donanımın yığıt sınırı değil, çalışma zamanının kendi koyduğu bir korumadır: gerçek yığıt tükenip süreç çökmeden önce denetimli bir hata üretilir. Koruması olmayan ortamlarda aynı program, işletim sisteminin sürece son vermesiyle sonuçlanır.
Kuyruk özyinelemesi, programlama temelleri kursunda tanımlandığı biçimiyle, bu sorunun bir çözümüdür: çağrı kuyruk konumundaysa yeni çerçeve açmak yerine var olan çerçeve yeniden kullanılabilir. Bu dönüşümü her gerçekleştirim yapmaz.
İş Parçacığı Başına Yığıt
Bir süreçte birden çok iş parçacığı çalışıyorsa, her birinin kendi yığıtı olmak zorundadır: çağrı zincirleri bağımsızdır ve bir iş parçacığının çerçeveleri diğerininkiyle karışamaz.
Öbek ise paylaşılır. Bu asimetri, eşzamanlı programlamanın temel gerilimini kurar: yığıttaki yerel değişkenler doğal olarak yalıtılmışken, öbekteki veriye birden çok iş parçacığı aynı anda erişebilir. Paylaşılan durumun korunması gereği buradan doğar ve eşzamanlılık, işletim sistemi kavramları kursunun konusudur.
Pratik bir sonuç da vardır: iş parçacığı başına ayrılan yığıt alanı, oluşturulabilecek iş parçacığı sayısını sınırlar.
Öbek
Bazı veriler, onları yaratan fonksiyondan uzun yaşamak zorundadır; boyutu ise ancak çalışma anında bilinir. Bunlar öbek (heap) üzerinde ayrılır.
Öbek yönetimi bir ayırıcı (allocator) tarafından yapılır. Ayırıcı, boş blokları izler; istenen boyuta uygun bir blok bulur, işaretler ve adresini döndürür. Serbest bırakılan blok yeniden kullanılabilir hâle gelir.
Bu esnekliğin üç maliyeti vardır:
- Ayırma pahalıdır. Uygun blok arama, yığıt işaretçisini kaydırmakla kıyaslanamayacak kadar çok iş içerir.
- Parçalanma oluşur. Ayır–bırak döngüleri, toplamı yeterli ama tek parça hâlinde yetersiz boşluklar bırakabilir.
- Yaşam süresi elle yönetilir. Serbest bırakılmayan blok sızıntıdır; serbest bırakıldıktan sonra kullanılan adres ise sarkan işaretçidir ve tanımsız davranış üretir.
Çöp toplayıcı kullanan dillerde üçüncü maliyet çalışma zamanına devredilir: erişilemez hâle gelen nesneler otomatik olarak toplanır. Bunun bedeli, toplama sırasında oluşan duraklamalar ve daha yüksek bellek kullanımıdır. Ödünleşim ortadan kalkmaz, yalnızca yer değiştirir.
Yaşam Süreleri
Üç ayrı yaşam süresi kuralı, üç bölmeye karşılık gelir:
| Yaşam süresi | Yer | Başlangıç | Bitiş |
|---|---|---|---|
| Statik | Veri bölmeleri | Program yüklenirken | Program bitince |
| Otomatik | Yığıt | Çerçeve açılınca | Fonksiyon dönünce |
| Dinamik | Öbek | Ayırma isteğiyle | Serbest bırakma veya toplama ile |
Bir programın hataları çoğu zaman bu sütunların karıştırılmasından doğar: otomatik ömürlü bir değere, ömrü bittikten sonra erişmek; dinamik ömürlü bir bloğu hiç bırakmamak; ya da statik ömürlü paylaşılan bir değeri birden çok iş parçacığından eşzamanlı değiştirmek.
Kursun Toparlanması
Bu noktada zincirin tamamı kurulmuştur. Bir program metni sözcüklere ayrıldı, ağaca dönüştü, ara gösterimden geçip makine komutlarına çevrildi; nesne dosyaları bağlandı, yükleyici bölmeleri adres uzayına yerleştirdi ve program sayacını giriş noktasına yazdı. İşlemci getir–çöz–yürüt döngüsünü çevirmeye başladı; komutlar yazmaçlar üzerinde çalıştı, veriler önbellek katmanları üzerinden taşındı, dış olaylar kesmelerle bildirildi. Ve tüm bu katmanların taşıdığı şey, ilk konunun konusu olan bit örüntüleriydi.
0x41424344 örüntüsü kurs boyunca beş kez okundu: işaretsiz tam sayı, işaretli tam sayı,
kayan noktalı sayı, ABCD metni ve bir komut olarak. Kursun tek bir cümleye indirgenmiş
sonucu budur: bellekte anlam yoktur; anlam, örüntüye uygulanan yorum kuralından
gelir. Bir programlama dilinin tip sistemi, bir dosya biçiminin tanımı ve bir ağ
protokolünün belgesi, hep aynı işi yapar — hangi kuralın geçerli olduğunu söyler.
Özet
- Sürecin adres uzayı kod, salt okunur veri, başlatılmış ve başlatılmamış veri, öbek ve yığıt bölmelerine ayrılır; yığıt ile öbek karşılıklı uçlardan büyür.
- Her fonksiyon çağrısı yığıta bir çerçeve ekler; çerçeve dönüş adresini, argümanları ve yerel değişkenleri taşır.
- Çerçeve ayırmak bir yazmaç kaydırmasıdır; bu yüzden ucuzdur, ancak yığıt sınırlıdır ve derin çağrı zincirleri onu tüketir.
- Öbek, boyutu veya ömrü çalışma anında belirlenen veriler içindir; ayırma pahalıdır, parçalanma ve yaşam süresi yönetimi maliyet getirir.
- Statik, otomatik ve dinamik yaşam süreleri farklı bölmelere karşılık gelir; yaygın bellek hataları bu üçünün karıştırılmasından doğar.
Sonraki Adım
Bu kurs, bir programın altındaki katmanları — veri temsili, işlemci, bellek ve derleme zinciri — kurdu. Bundan sonraki kurs, katmanın üstüne geçer: Programlama Temelleri, değişken, denetim akışı, fonksiyon ve özyineleme kavramlarını dilden bağımsız olarak ele alır. Bu kursta yalnızca örnek olarak kullanılan kod parçaları, orada dersin konusunun kendisi olacaktır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.