> İşlemcilerin Sayfalama Mekanizması: Masaüstü (desktop) işletim sistemlerinin çalıştığı kapasiteli işlemlerin "sayfalama (paging)" denilen bir mekanizması vardır. Sistem programcılarının bu sayfalama mekanizmasını temel düzeyde biliyor olmaları gerekir. Sayfalama mekanizması "sanal bellek (virtual memory)" denilen bellek yönetim tekniği için de bir araç durumundadır. Intel işlemcileri 80386 ile birlikte (ilk kez 1985) sayfalama mekanizmasına sahip olmuştur. ARM işlemcilerinin A'lı (Application) serisi cortex'leri bu mekanizmaya sahiptir. PwerPC; Itanimum, Alpha gibi yaygın işlemlerin de sayfalama mekanizması bulunmaktadır. Ancak düşük kapasiteli işlemcilerde ve mikrodenetleyicilerde genel olarak bu mekanizma yoktur. Örneğin ARM işlemcilerinin M'li (Microcontoller) serisi genel olarak sayfalama mekanizmasına sahip değildir. Şimdi de adım adım bu konu hakkındaki detayları inceleyelim: >> Sayfa (page) bellekteki ardışıl byte topluluklarına denilmektedir. Sayfalama mekanizmasında işlemci RAM'e byte düzeyinde erişebilir. Ancak aynı zamanda RAM'i sayfalardan (bloklardan) oluşan bir birim gibi ele almaktadır. Sayfa büyüklükleri farklı işlemcilerde farklı olabilmektedir. Bazı işlemciler farklı büyüklükteki sayfaları destekleyebilmektedir. Ancak işlemcilerin desteklediği en yaygın kullanılan sayfa büyüklüğü 4K'dır. * Örnek 1, Windows, Linux, macOS sistemleri işlemcinin 4K sayfalama mekanizmasını kullanmaktadır. Bu nedenle biz kursumuzda sayfa denildiğinde onun büyüklüğünün default olarak 4K olduğunu varsayacağız. >> İşlemciler RAM'in her sayfasına ilk sayfa 0 olmak üzere bir numara verirler. Örneğin RAM'in tepesindeki ilk 4096 byte 0'ıncı sayfa, sınraki 4096 byte 1'inci sayfadır ve bu böyle devam etmektedir. Bir adres aslında RAM'de belirli bir sayfanın belirli bir offset'indedir. Burada "offset" demekle ilgili sayfanın başından itibaren olan uzaklığı kastediyoruz. Sayfa uzunluğunun 4K olduğunu varsayalım (gerçekte uygulanan tipik durum).İşlemcinin belli bir adresin hangi sayfada o sayfanın hangi offset'inde olduğunu anlaması oldukça kolaydır. Adresin sağdaki 12 biti sayfa offsetini verir. Adres değeri 12 kere sağa ötelenirse bu da adresin sayfa numarasını verecektir. Örneğin 3F7C42 biçiminde bir adres olsun. Bu adres RAM'in 3F7'inci sayfasındadır ve bu sayfanın C42'inci offset'indedir: 3F7 ==> sayfa numarası C42 ==> Sayfa offset'i >> Sayfalama mekanizmasını kullanan işletim sistemleri programları ardşıl bir biçimde RAM'e yüklememektedir. Program işletim sistemi tarafından sayfa büyüklüklerine göre parçalara ayrılır, parçalar boş sayfalara yüklenir. * Örnek 1, elimizde 20K'lık bir program olsun. Eğer sistemde sayfalama mekanizması olmasaydı bu 20K ardışıl bir biçimde RAM'e yüklenecekti. Ancak sayfalama mekanizması söz konusu olduğunda bu 20K'lık program 4K'lık 5 parçaya ayrılır ve bu 5 parça ardışıl olmayabilen 5 boş syfaya yüklenir. Böylece 20K'lık programın 4'er K'lık parçaları farklı sayfalarda bulunuyor olacaktır. Örneğin fiziksel belleğin belli bir bölümünün sayfaları aşağıdaki gibi olsun: Sayfa Numarası Sayfanın Durumu ... ... 3FC5 3FC6 3FC7 3FC8 3FC9 3FCA 3FCB 3FCC 3FCD 3FCE 3FCF 3FD0 ... .... Şimdi işletim sistemi programın sayfalarını boş sayfalara yğkleyecektir. Şöyle bir durum oluşabilecektir: Sayfa Numarası Sayfanın Durumu ... ... 3FC5 3FC6 Programın 1'inci Parçası 3FC7 3FC8 3FC9 Programın 2'inci Parçası 3FCA Programın 3'üncü Parçası 3FCB 3FCC 3FCD Programın 4'üncü Parçası 3FCE 3FCF Programın 5'inci Parçası 3FD0 ... .... Burada iki soru karşımıza çıakacaktır: -> Birincisi programın ardışıl yüklenmemesinin avantajı nedir? -> İkincisi de programın hangi parçasının hangi sayfalarda olduğunu işletim sistemi nasıl bilmektedir? Sayfalama mekanizmasına sahip işlemciler çalışırken ismine "sayfa tablosu (page table)" denilen bir tabloya balarak çalışırlar. Sayfa tablosu işletim sistemi tarafından her proses için ayruı bir biçimde oluşturulur ve Proses Kontrol Bloğunda tutulur. İşlemciler sayfa tablolarını belli bir yazmacın gösteridği yerde ararlar. Böylece işletim sistemi prosesin sayfa tablosunu oluşturur. İşlemcinin ilgili yazmacına sayfa tablosunun adresini yerleştirir. İşlemci de o sayfa tablosunu kullanır. >> Bugünkü işletim sistemleri daha önceden de belirttiğimiz gibi thread temelinde preemptive zaman paylaşımlı sistemlerdir. Bir thread işlemciye atanır. Belli bir süre çalıştırılır, sonra çalışmasına ara verilip diğer bir thread işlemciye atanır. Eğer threadler arası geçiş (context switch) sırasında ara verilen thread ile geçilen thread farklı proseslerin thread'leri ise işletim sistemi işlemcinin ilgili yazmacındaki değeri değiştirerek işlemcinin yeni geçilen thread'in ilişkin olduğu prosesin sayfa tablosunu kullanmasını sağlar. Yani belli bir anda hangi program çalışıyorsa işlemci onun sayfa tablosunu kullanıyor durumda olur. Intel İşlemcilerinde sayfa tablosunun yeri CR3 yazmacıyla belirlenmektedir. Sayfa tablosu aslında sanal sayfaları fiziksel sayfalara eşleyen bir tablodur. Sayfa tablosunun kaba biçimi aşağıdaki gibidir: Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 3FC6 4001 3FC9 4002 3FCA 4003 3FCB 4004 3FCF ... .... Buradan hareketle, * Örnek 1, 32 bitlik bir sistemde derleyici sanki program 4GB'lik boş bir RAM'e tek başına yüklenecekmiş gibi kod üretmektedir. Yani 32 bit Windows sistemlerinde derleyici ve linker sanki program boş bir 4GB'lik belleğin 4MB'sinden itibaren yüklenecekmiş gibi kod üretir. İşletim sistemi programı 4K'lık sayfalara ayırıp anları o parçaları boş sayfalara yerleştirmektedir. Böylece programın sanal adres alanı ardışılmış gibi bir durum oluşturulur. İşlemci sayfa tablosuna bakarak çalıştığı için bir sorun çıkmamaktadır. İşlemci karşılaştığı her adresi "sayfa numarası" ve "sayfa offset'i" biçiminde iki parçaya ayırmakta, sonra sayfa numarasını sayfa tablosundan fiziksel sayfa numarasına dönüştürüp aslında o fiziksel sayfada ilgili offset'e erişmektedir. Örneğin işlemci aşağıdaki gibi bir makine komutu ile karşılaşmış olsun: MOV EAX, [4003F12] Burada 4003F12 adresi 4003 sayfa numarasının F12 offset'ini belirtmektedir. İşlemci sayfa tablosunda 4003 numaralı sayfanın 3FCB numaralı fiziksel sayfayla eşleştirildiğini görür aslında fiziksel bellekte 3FCBF12 adresine erişir. >> Program içerisindeki bütün adresler gerçek fiziksel adresler değildir. Bunlara "sanal adres (virtaul address)" ya da "doğrusal adres (linear address)" denilmektedir. İşlemci sanal adresleri fiziksel adreslere dönüştürüp fiziksel RAM'de gerçek yere erişmektedir. Prosesin sanal adres alanının ardışıl olduğuna ancak fiziksel adres alanının ardışıl olmadığına dikkat ediniz. Bu durumda iki programdaki aynı sanal adresler aslında aynı fiziksel adres belirtmemektedir. Çünkü işletim sistemi her prosesin sayfa tablosunda sanal adresleri farklı fiziksel sayfalara yönlendirmektedir. * Örnek 1, aynı programı ikinci kez çalıştırdığımızda aslında program içerisindeki sanal adresler değişmez. Çünkü her program sanal belleğe onun belli bir noktasından itibaren tek başına yüklenecekmiş gibi koda sahiptir. Aşağıdaki örnekte 20K uzunluğunda bir programın ikinci kez çalıştırımasındaki sayfat atblosu temsili olarak verilmiştir. Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 3FC6 4001 3FC9 4002 3FCA 4003 3FCB 4004 3FCF ... .... Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 4F12 4001 7B14 4002 F12A 4003 47C8 4004 16CB ... .... Burada örneğin 40013FC adresi her iki proseste aynı sanal adres olsa da farklı fiziksel adres belirtmektedir. >> Sayfalama mekanizmasının kullanıldığı sistemlerde işletim sistemi zaten prosesleri sayfa tabloları yoluyla izole etmektedir. Yani bir iki prosesin sayfa tablolarındaki fiziksel sayfa adresleri zaten farklıdır. Bu durumda bir proses diğerinin fiziksel sayfalarına zaten erişemez. Sayfalama "sanal bellek (virtual memory)" denilen bellek yönetim tekniğini uygulanması için gerekli bir mekanizmadır. Sanal bellek bir programın tamamının değil yalnızca belli sayfalarının RAM'e yüklenip disk ile RAM arasında yer değiştirmeli biçimde çalıştırılması anlamına gelmektedir. Bugün Windows, Linux, macOS gibi işletim sistemleri sanal bellek mekanizmasını kullanmaktadır. Sanal bellek mekanizmasında işletim sistemi prosesin tüm sayfalarını fiziksel RAM'e yüklemez. Yalnızca bazı sayfalarını yükleyerek programı çalıştırır. Yani sayfa tabşlosunda bazı sanal sayfalara fiziksel bir sayfa karşılık düşürülmemiştir. * Örnek 1, Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 3FC6 4001 - 4002 3FCA 4003 - 4004 3FCF ... .... Buradaki '-' karakterleri ilgili sanal sayfa için bir fiziksel sayfanın karşı düşürülmediğ anlamına gelmektedir. Pekiyi işlemci böyle bir durumla karşılaştığında ne yapmaktadır? * Örnek 1, işlemci aşağıdaki gibi bir makine komutuyla karşılaşsın: MOV EAX, [400117C] Bu makine komutunda belleğin 400117C adresindeki 4 byte CPU yazmacına çekilmek istenmektedir. Komuttaki adresin sanal sayfa numarası 4001'dir. İşlemci sayfa tablosuna başvurup bu sanal sayfa için bir fiziksel sayfanın karşı gelmediğini gördüğünde bir "içsel kesme (internal interrupt)" oluşturmaktadır. Bu içsel kesmeye "page fault" da denilmektedir. >> Page fault oluşturğunda işlemci sistem programcısının belirlediği bir kodu çalıştırır. Bu koda "page fault handler" denilmektedir. Page fault handler işletim sistemini yazanlar tarafından yazılmıştır. Böylece page fault oluştuğunda aslında otomatik olarak işletim sisteminin bir kodu devreye girmektedir. İşletim sisteminin bu page fault kodu önce buna yol açan adresi incelemektedir. Bu kod söz konusu adresteki adresteki sanal sayfa numarısına bakar. Bunun programın neresine (yani hangi 4K'lık kısmına) karşı geldiğini tespit eder. Programın o 4K'lık kısmını diskten alarak boş bir sayfaya yükler. Sonra sayfa tablosunu düzeltir. Artık sayfa tablosunda ilgili sayfaya karşı bir fiziksel sayfa bulunmaktadır. Sonra kesme kodunu sonlandırır. Page fault kodunun çalışması bittiğinde işlemci bu fault'a yol açan makine komutuyla çalışmasına devam eder. Dolayısıyla artık ilgili adresin sanal sayfa numarası fiziksel bir sayfa numarasına yönlendirilmiş durumdadır. Kullanıcılar programın kesiksiz çalıştığını sanmaktadır. Aslında program ilgili sayfaların gerektiğinde diskten RAM'e yüklenmesiyle çalışmaktadır. (Örneğin diskinizin ışığına bakarsanız onun sürekli yanıp söndüğünü fark edersiniz. Çünkü programlar çalışırken sürekli "page fault" oluşmaktadır.) * Örnek 1, Yukarıdaki örneğimizde 400117C adresine erişldiğinde oluşan page fault'ta işletim sisteminin programın ilgili 4K'lık parçasını RAM'de boş olan 418F numaralı fiziksel sayfaya yüklediğini düşünelim. Artık sayfa tablosunun ilgili kısmı şöyle olacaktır: Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 3FC6 4001 418F 4002 3FCA 4003 - 4004 3FCF ... .... Pekiyi page fault oluştuğunda fiziksel RAM'deki tüm sayfalar doluysa ne olacaktır? Bu durumda işletim sistemi dolu olan bir sayfayı fiziksel RAM'den atacak ve programa ilişkin 4K'lık kısmı bu bu fiziksel sayfaya yükleyecektir. Tabii işletim sistemi "ileride en az kullanılabilecek" bir sayfayı RAM'den atmaya çalışmaktadır. Bunun için kullanılan algprtimalara "page replacement" algoritmaları denilmektedir. En çok tercih edilen "page replacment" algoritması "LRU (Least Recently Used)" algoritmasıdır. Bit fiziksel sayfanın RAM'den atılmasına İngilizce "swap-out", diskteki bir sayfanın RAM'e çekilmesine ise "swap-in" denilmektedir. Disk ile RAM arasındaki bu tür değiştirmelere ise "swapping" ismi verilmektedir. Pekiyi swap-out işlemi yapılacakken ya RAM'deki sayfanın içeriği değişmişse ne olacaktır? Eğer RAM'deki sayfanın içeriği değişmemişse ve bu sayfa çalıştırılabilir dosyanın (executable file) içerisinde zaten varsa o sayfa doğrudan RAM'den atılabilir. Çünkü gerektiğinde yine çalıştırılabilir dosyanın içerisinde alınabilecektir. Ancak eğere RAM'deki sayfa değişmişse (buna "dirty" hale gelmek de denilmektedir) bu durumda o sayfa daha sonra yeniden swap-in yapılabileceği için sayfanın diskte saklanması gerekir. İşte güncellenmiş olan (kirlenmiş olan) bu tür sayfaların diskte saklanabilmesi için işletim sistemleri "swap dosyası (swap files)" oluşturmaktadır. Tabii işletim sistemlerinin oluşturduğu swap dosyalarının da bir büyüklüğü vardır. Eğer o büyüklük aşılırsa sanal belleğin de limitine ulaşılmış olur. O halde kabaca aslında sanal belleğin toplam büyüklüğü kabaca "fiziksel RAM + swap dosyası" kadardır. Swap dosyaları genellikle işletim sisteminin çalışma zamanı sırasında dinamik olarak büyütülmezler. Bunlar için genellikle baştan bir yer ayrılmaktadır. Bu yerin büyüklüğünü sistem yöneticisi belirleyebilmektedir. * Örnek 1, Windows sistemlerinde "Denetim Masası/System/Advanced System Settings/Performance/Advanced/Virtual Memory" penceresinden değiştirilebilmektedir. * Örnek 2, Linux sistemleri bu bakımdan da esnektir. Genellikle Linux sistemlerinde swap dosyası bir "disk bölümü (disk paritition)" olarak ayrılır. Ancak sistem yöneticisi isterse disk dosyası yaratıp onu da swap alanına dahil edebilmektedir. Bu sistemlerde "free" komutu ile ya da "swapon -s" komutu ile swap alanlarının listesi alınabilmektedir. Linux'ta bir dosyanın swap alanına dahil edilmesi için ne yapılması gerektiğine ilgili dokümanlardan erişebilirsiniz. >> Pekiyi sanal bellek kullanan sistemlerde dinamik bellek tahsisatı nasıl yapılmaktadır? Dinamik alan çalıştırılabilir dosyanın içerisinde olmadığına göre ve bunun için swap dosyası içerisinde bir yer ayrılmalıdır. malloc gibi bir fonksiyonu çağırdığımızda bu fonksiyon swap dosyası içerisinde sayfalar için yer ayırmakla birlikte gerçek fiziksel RAM'de henüz bir tahsisat yapmamaktadır. Dinamik tahsis edilen alanın sayfalarına erişildiğinde swap-in yapılmaktadır. Biz eldeki fiziksel RAM'im ötesinde dinamik tahsisatlar yapabiliriz. Ancak tabii swap dosyalarımızın toplam uzunluğu da bizim için bir limit oluşturmaktadır. Pekiyi işletim sistemi bir sayfanın güncellendiğini (kirlendiğini) nasıl anlamaktadır? İşte aslında sayfa tablosunda her sayfanın bilgilerinin tutulduğu bir yer de vardır. Yani sayfa tablosunun organizasyonu aşağıdaki gibidir: Sanal Sayfa No Fiziksel Sayfa No Sayfa Özellikleri ... ... ... 4000 3FC1 read-write-user 4001 1FC2 read-kernel 4002 4D02 read-write-user ... ... ... >> İşlemci ne zaman bir sayfaya erişse o sayfanın sayfa tablosundaki "D (Dirty)" bitini set etmektedir. İşletim sistemi de baştan bu reset eder sonra swap-out yapacağı zaman bu bite bakar. eğer bu bit set edilmişse sayfanın güncellenmiş olduğunu düşünür. Sayfalara "read only" ya da "read/write" biçiminde özellikler verilebilmektedir. "read-only" bir sayfaya yazma yapıldığında "page fault" oluşmaktadır. Böyle bir durumda işletim sistemi prosesi cezalandırıp sonlandırmaktadır. * Örnek 1, C derleyicileri string ifadelerini "read-only" section'lara yerleştirmektedir. İşletim sistemi de bu sectionlar'ı RAM'e sayfa sayfa yüklemektedir. String'lerin bulunduğu bu sayfaların sayfa özelliklerini "read-only" yapmaktadır. Dolayısıyla Windows sistemlerinde, Linux ve macOS sistemlerinde bir string'in herhangi bir karakterini değiştirmek istediğimizde page fault oluşmakta bunun sonucu olarak da prosesimiz işletim sistemi tarafından sonlandırılmaktadır. Zaten C'de bir string'in karakterlerini değiştirmek "tanımsız davranışa" yol açmaktadır. >> Sayfaların diğer bir özelliği de "user mode/kernel mode" özelliğidir. Bir sayfa "user mode" özelliğindeyse o sayfaya user modda çalışan prosesler ve kernel modda çalışan (örneğin işletim sistemi) prosesler erişebilir. Ancak sayfa eğer kernel modda ise o sayfaya yalnızca kernel modda çalışan prosesler erişebilir. User modda çalışan prosesler kernel mod özelliğine sahip sayfalara erişmek istediğinde page fault oluşmaktadır. İşletim sistemi de bu prosesleri cezalandırarak sonlandırmaktadır. >> Bir proses çalışırken işletim sisteminin kodları da sayfa tablosunda fiziksel sayfalara yönlendirilmiş durumdadır. Çünkü proses sistem fonksiyonlarını çağırdığında akışın işletim sisteminin içerisine girip o sistem fonksiyonun kodlarını çalıştırabilmesi için işletim sisteminin kodlarının bulunduğu sayfalarında sayfa tablosunda bulunuyor olması gerekir. İşletim sistemi kendisini user mode proseslerden korumak için kendi kodlarının bulunduğu sayfaları kernel mode sayfa olarak özelliklendirir. Böylece user mode programlar kernel mode sayafalara erişmek istediğinde page fault oluşmakta ve proses sonlandırılmaktadır. Sistem fonksiyonları çağrıldığında prosesin çalışma modunun user mode'dan kernel mode'a geçtiğini anımsayınız. Pekiyi işletim sistemi farklı iki prosesin farklı sanal sayfalarını aynı fiziksel sayfaya yönlendirirse ne olur? * Örnek 1, Aşağıda iki prosesin sayfa tablosuna temsil bir örnek verilmiştir: Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 3FC6 4001 56F1 4002 3FCA 4003 26C3 4004 3FCF ... .... Sanal Sayfa No Fiziksel Sayfa No ... ... 4000 246C 4001 17F3 4002 5421 4003 5421 4004 3FC6 ... .... Burada birinci prosesin 4000 numaralı sanal sayfası ve ikinci prosesin 4004 numaralı sanal sayfası 3FC6 fiziksel sayfsına yönlendirilmiştir. O halde birinci proses 4000000-400FFF sanal adreslerine bir şey yazdığında diğer proses bu yazılanları 4004000-4004FFF sanal adreslerinden okuyabilecektir. Yani iki prosesin farklı sanal sayfaları aslında aynı fiziksel sayfayı görmektedir. Bu biçimdeki proseslerarası haberleşme yöntemine "shared memory" denilmektedir. Şimdi aynı programı ikinci kez çalıştırdığımızı düşünelim. Bu durumda aslında yanı kodlar farklı bir proses olarak kullanılacaktır. Bu tür durumlarda işletim sistemi başlangıçta mümkün olduğunca ikinci kopyası çalıştırılan prosesin sayfa tablosundaki girişleri birinci kopyanın kullandığı fiziksel sayfalara yönlendirmektedir. Yani işletim sistemi iki prosesin sayfa tablosundaki fiziksel sayfa numaralarını mümkün olduğunca aynı yapar. Tabii bu prosesler biribirinden farklı olduğuna göre proseslerden birinin fiziksel sayfaya yazma yaptığında diğerinin bu yazmadan etkilenmemesi gerekir. Pekiyi bu nasıl sağlanacaktır? İşte işletim sistemleri başlangıçta aynı fiziksel sayfaları kullanan proseslerin birinin bu fiziksel sayfaya yazma yaptığı zaman bu fiziksel sayfanın kopyasını çıkarmaktadır. Yani iki proses (daha fazla da olabilir) başlangıçta aynı fiziksel sayfayı kullanıyor olsa da bir yazma olayı gerçekleştiğinde artık bu fiziksel sayfalar birbirinden ayrılmaktadır. Bu mekanizmaya işletim sistemlerinde "copy on write" denilmektedir. >> Pekiyi "copy on write" mekanizmasını işletim sistemi nasıl gerçekleştirmektedir. Genellikle kullanılan yöntem şöyledir: İşletim sistemi aslında read-write özelliğe sahip olan sayfalara "copy on write" uygulayabilmek için "read-ony" özellik atamaktadır. Bu sayfaya proseslerden biri yazma yapmaya çalıştığında "page fault" oluşmakta ve işletim sistemi devreye girip sayfanın kopyasını çıkartmaktadır. Tabii artık işletim sistemi kopyası çıkartılan sayfanın özelliklerini "read-write" olarak değiştirecektir. * Örnek 1, UNIX/Linux sistemlerinde fork işlemi sırasında aslında baştan tüm prosesin bellek alanının gerçek bir kopyası oluşturulmamaktadır. Alt prosesin sayfa tablosunun içeriği üzt prosesteki gibi yapılmaktadır. Ancak alt ya da üst proseslerden biri bu sayfalardan birine yazma yaptığında o sayfanın kopyasından çıkarılmaktadır. Bu nedenle fork sonrasında alt proseste exec işlemi yapıldığında aslında bunun bellek kopyalama maliyeti oluşmamaktadır. Zaten artık exec öncesinde cfork kullanılmamasının bir nedeni de budur.