Virtual Memory nedir ve kullanım alanları nelerdir?

Bu flood’da Virtual Memory sayesinde diskin nasıl hafıza gibi kullandığını ve Virtual Memory’nin diğer kullanım alanlarını anlatacağım.

Önceki bir flood’da Virtual Memory sayesinde Copy-on-Write tekniğinin geliştirildiğini ve Virtual Memory’nin birçok başka kullanım alanı bulunduğunu belirtmiştik. Virtual Memory temel olarak işletim sistemi tarafından fiziksel hafızanın uygulamadan soyutlanması demektir. İşletim sistemi fiziksel hafızayı (örneğin 4KB’lik) bölümlere ayırır ve her birini page olarak adlandırır. İşletim sistemi, hafızayı kullanmak isteyen programlara bu page'leri direkt tahsis etmek yerine bu page'lere karşılık gelen Virtual Memory alanlarını tahsis eder. Virtual Memory ve fiziksel memory page'ler arasındaki dönüşüm MMU (Memory Management Unit) adlı hardware ile yapılır.

İşletim sistemleri, istendiği taktirde programları çalıştırırken hafıza bakımından over-provisioning yapar. Örneğin 4GB RAM'e sahip bir bilgisayarda toplam ihtiyacı 10GB olan programları aynı anda çalıştırabiliriz. İşletim sistemleri bu noktada kendilerine sabit disk üzerinde ayrılan alanı page'lere bölerek bu alanları da Virtual Memory tablolarına kaydederler. Mevcut fiziksel hafızanın tamamen kullanıldığı durumda CPU tarafından koşturulmaya başlanan programın aktif page'leri diskten fiziksel hafızaya taşınır, fiziksel hafızada yer açmak içinse o ana kadar en az kullanılan page'lerden ihtiyaç duyulan kadarı disk'e gönderilir. Görüleceği üzere hafıza ihtiyacı çok yüksek olan programlar düşük hafızaya sahip bir bilgisayarda çalıştırıldığında disk ve fiziksel hafıza arasında çok fazla geçiş olacak, bu da disk yavaş tepki süresi olan bir donanım olduğundan performansı olumsuz yönde etkileyecektir. Fiziksel hafızda bulunmayan bir page'in diskten alınması için page fault interrupt'ı (kesmesi) MMU tarafından oluşturulur ve işletim sistemi tarafından yakalanarak işlenir. İşletim sisteminde page fault yoğunluğuna bakılarak performans probleminin diskteki swap kullanımı kaynaklı olup olmadığı bulunabilir. Bahsedilen bu mekanizmaya ilişkin detaylı bilgi için MMU ve TLB (Translation Lookaside Buffer) anahtar kelimeleri ile arama yapılabilir.

Yukarıda anlatılan mekanizma aşağıda görselleştirilmiştir.

Şimdi de Unix/Linux işletim sistemlerinde Segmentation Fault, Windows işletim sisteminde ise son sürümlerde Access Violation ve önceki sürümlerde Bu program geçersiz bir işlem yürüttü ve kapatılacak hatasını neden aldığımızı anlamaya çalışalım.

İşletim sistemi, Virtual Memory ile page'lere ayırdığı hafıza alanlarını (aksi belirtilmedikçe) sadece tahsis ettiği program erişecek şekilde ayarlar. Böylece bir programın erişebileceği hafıza alanları belirlenir ve prosesler kendi adres space'lerinde olmayan bir hafıza alanına erişmek istediklerinde MMU tarafından işletim sistemine invalid türünden bir page fault kesmesi üretilir, işletim sistemi de Unix/Linux'ta ilgili prosese Segmentation Fault - SIGSEGV sinyalini gönderir, bu sinyalin default davranışı ise ilgili prosesin sonlandırılmasıdır. Gördüğünüz gibi işletim sistemi programınızın hafıza alanını başka programların ezmesine karşı korumaktadır :-)

Segmentation Fault çoğu durumda bir programlama hatasını işaret eder. C/C++ gibi Unmanaged dillerde Dangling Pointer adı verilen ve gerçek bir objeyi göstermeyen pointer sebebiyle olabileceği gibi yine C/C++ gibi dillerde bir hafıza alanına (genellikle array) indeksi şaşırarak tahsis edilenden daha fazla eleman koymak (buffer overflow) veya stack overflow durumlarından kaynaklanabilir.

Son olarak C/C++ gibi Unmanaged dillerde buffer overlow bulmak için kullanılan efence (benzeri valgrind) programının nasıl çalışığını anlatarak bitirelim. İşletim sistemi, kendi programımıza ait olmayan bir page'e geçtiğimizde prosesimizi öldürse de kendi page'imiz içerisinde programlama hatasından dolayı yaptığımız buffer overflow'ları tespit edemez. Programı geliştirme ve test aşamasında efence ile çalıştırılarak bu tür hataların çoğunu bulabiliriz.

efence, aşağıda gördüğünüz gibi prosesimizin yaptığı bütün hafıza isteklerini bir page'in sonuna denk getirerek karşılar ve bu page'den sonra ekstra bir page daha allocate ederek yeni page'i prosesimiz için erişilemez hale getirir.

efence'in erişilemez olarak işaretlediği page sayesinde prosesimiz buffer'ın ilerisinde bir yere yazmaya başladığı anda işletim sistemi tarafından SIGSEGV ile durdurulur ve biz stacktrace'den problemi tespit edebiliriz. efence kullanmasaydık buffer'daki taşma sebebiyle başka bir değişken üzerine yazacaktık ve muhtemelen program o değişkenin hatalı değerinden dolayı hatalı çalışacaktı, bizse problemi nerede arayacağımızı bilemeyeceğimiz için uzunca bir süre harcayacaktık :)