Esen,
Bu yazımda Assembly ya da C++ kodlarken karşımıza sıklıkla çıkan bellek özgüleme -memory allocation- yordamları hakkında bilgiler ve kaynak kodlar vermeye çalışacağım. MSDN ağında Memory Mamagement Functions – Bellek Yönetim Yordamları adı altındaki bette windows için kullanılacak tüm bellek yordamları listelenmiş. Gönül isterdiki hepsini uzun uzun örneklerle anlatayım ama öyle bir zaman dilimi yok. Ben burada en çok kullanacaklarımızı anlatmaya özen gösteriyorum. Daha çok ayrıntı isteyenler o beti incelesinler.
GlobalAlloc, LocalAlloc, HeapAlloc en sonunda heap-yığıt ‘tan bellek alanı ayırma işlemi yapan ve aralarında ufak bazı farklılıklar olan yordamlardır. 32 bit Windows ile birlikte başlayan GlobalAlloc ve LocalAlloc yordamları özünde HeapAlloc yordamının “wrapper” yani “sarmalayan” yordamlarıdır. Bu şu anlama geliyor GlobalAlloc ve LocalAlloc bellekte ayırma işlemi yaparken aslında HeapAlloc yordamını kullanmaktadır.Farklı yığıt bölüştürücüleri yine farklı mekanizmalar kullanarak belirgin işlevsellik sağladıkları için doğru yordamla bellek alanını serbest bırakmak gerekmektedir. HeapAlloc için HeapFree, LocalAlloc için LocalFree ve GlobalAlloc için GlobalFree kullanılmalıdır.
HeapCreate
Çağıran proses HeapAlloc ile bellek blokları ayırabilsin diye HeapCreate özel bir yığıt-heap nesnesi yaratır. HeapCreate yordamı başarılı bir şekilde çalışırsa yarattığı yığıt-heap ‘in HANDLE değerini döndürür. HeapAlloc’da bu HANDLE değerini kullanarak bellek blokları ayırabilir.
HeapCreate ile heap-yığıt ayırma işlemini pek kullanmıyoruz. Çünkü işletim sistemi zaten bir tane yaratmış oluyor. GetProcessHeap APIsi ile işletim sisteminin yarattığı yığıtın HANDLE değerini döndürüp işlem yapabiliriz.
HANDLE WINAPI HeapCreate( _In_ DWORD flOptions, _In_ SIZE_T dwInitialSize, _In_ SIZE_T dwMaximumSize );
flOptions yığıt ayırma seçenekleridir. Bu seçeneklere göre heap yaratılıp ve bundan sonra yapılacak ayırma işlemlerini etkiler. Bu seçenek 0 veya aşağıdaki seçeneklerden birisi olabilir.
12345678 <span style="font-size: 11px;">HEAP_CREATE_ENABLE_EXECUTE0x00040000HEAP_GENERATE_EXCEPTIONS0x00000004HEAP_NO_SERIALIZE0x00000001</span>
dwInitialSize bayt türünde heap-yığıt’ın başlangıç değeridir. dwMaximumSize’dan küçük olmalıdır. NULL değeri alabilir. Eğer null olursa sadece bir sayfa yaratır.
dwMaximumSize bayt türünden heap-yığıtın maximum büyüklüğüdür. Eğer HeapAlloc ya da HeapReAlloc yordamları dwInitialSize’da belirtilen değeri aşarsa sistem dwMaximumSize’da belirtilen değere kadar heap-yığıt boyutunu genişletir.
Bu değer NULL olabilir. Biz de genelde bunu kullanacağız. Böylece bir üst sınır vermemiş oluyoruz. Heap-yığıt büyüyebiliyor. Heap-Yığıt’ın sınırı kullanılabilir/uygun/müsait bellek alanı ile sınırlanmış oluyor. Büyük bellek boyutları ayrılacaksa kesinlikle bu değişkeni 0 yapmalıyız.
Bu değer sıfır değilse bir üst değer vermiş ve heap-yığını kısıtlamış oluyoruz. Bu üst sınırdan daha fazla yığıtımız büyüyemiyor.
Eğer yordam başarısız olursa 0 döndürür. Sonrasında GetLastError yordamını çağırarak ne hatası olduğunu döndürebiliriz.
HeapAlloc
Heap-yığıttan bellek bloğu ayırır. Ayrılan bellek hareketsizdir.
LPVOID WINAPI HeapAlloc( _In_ HANDLE hHeap, _In_ DWORD dwFlags, _In_ SIZE_T dwBytes );
hHeap ayıracağımız bellek alanının HANDLE değeri.HeapCreate ve GetProcessHeap tarafından döndürülmüş olabilir.
dwFlags heap-yığıt ayırma seçenek bayraklarıdır. Burada yapılacak seçimler HeapCreate ile yaratılırken yapılan seçimleri geçersiz kılar. Aşağıdaki seçeneklerden biri ya da bir kaçı seçilebilir.
123456789 <span style="font-size: 11px;">HEAP_GENERATE_EXCEPTIONS0x00000004 ;olağan dışı bir durum olduğunda NULL döndürmesin;onun yerine istisna-exception kodu döndürsünHEAP_NO_SERIALIZE0x00000001HEAP_ZERO_MEMORY0x00000008 ;ayrılan bellek alanı 0 olarak ayrılsın</span>
dwBytes bayt türünden ayrılacak yığıt alanı. Eğer hHeap büyüyemeyen bir yığıtın HANDLE değeriyse dwBytes 0x7FFF8’da küçük olmalıdır.
Yordam başarıyla çalıştığında ayrılan bellek bloğunun adresinin göstergeci(pointer) döndürülür.
Yordam başarısız olurda NULL değeri döner. Eğer HEAP_GENERATE_EXCEPTIONS seçilmişse GetExceptionCode makrosunun kullandığı WinBase.h header dosyasından bulunan döüş kodlarını döndürür. Örnek olarak:
1234 <span style="font-size: 11px;">STATUS_NO_MEMORY ;yeterli yığıt olmadığı;için ayırma işlemi hatasıSTATUS_ACCESS_VIOLATION ;uygun olmayan yordam parametreleri;nedeniyle erişim hatası verebilir</span>
Yordam hata verirse SetLastError APIsini çağırmaz, o yüzden hata kodunu GetLastError ile genişletilmiş hata bilgisine ulaşılmaz.
HeapFree
HeapAlloc veya HeapReAlloc ile yığıttan ayrılan bellek alanını serbets bırakır. lpMem ayrılan bellek alanının poinbter-göstergecidir. Bir kere serbest bırakıldıktan sonra içindekiler sonsuza kadar gider geri döndürülemez. Yani aynı pointer birkez daha kullanılmaz. Gerekiyorsa HeapAlloc ile tekrar bellek alanı ayrılır.
BOOL WINAPI HeapFree( _In_ HANDLE hHeap, _In_ DWORD dwFlags, _In_ LPVOID lpMem );
hHeap serbest bırakılacak bellek alanının içinde bulunduğu yığıtın HANDLE değeri
dwFlags heap-yığıt serbest bırakma seçenekeleri. NULL serilebilir.
lpMem HeapAlloc veya HeapRealloc tarafından yığıtta ayrılan ve şimdi serbest bırakılmak istenen bellek alanının göstergeci(pointer).
Yordam başarılı olursa dönüş değeri sıfır olmaz.
Yordam başarısız olursa dönen değer 0 olur. GetLastError ile hatayı döndürebiliriz.
HeapDestroy
hHeap HANDLE değeriyle belirtilen yığıt nesnesini yok eder. Parametre olarak verilen HANDLE değerini geçersiz kılar.
BOOL WINAPI HeapDestroy( _In_ HANDLE hHeap );
hHeap yok edilecek yığıt’ın HANDLE değeri. Bu handle yukarıda anlattığım HeapCreate yordamı tarafından döndürülmüştü. GetProcessHeap yordamı tarafından döndürülen yığıt handle’ını bu yordam ile yok etmiyoruz.
GlobalAlloc
Belirtilen bayt büyüklüğü kadar alanı yığıttan ayırır.
HGLOBAL WINAPI GlobalAlloc( _In_ UINT uFlags, _In_ SIZE_T dwBytes );
uFlags bellek ayırma özelikleridir. Eğer 0 seçilirse GMEM_FIXED varsayılan değer olarak alınır.
12345678910111213141516 <span style="font-size: 11px;">GMEM_FIXED0x0000 ;sabit bellek alanı seçeneği;dönüş değeri göstergeçtirGMEM_MOVEABLE0x0002 ;hareketli bellek alanı seçeneği;fiziksel bellekde hareket etmez;varsayılan yığıt içinde hareket eder;DÖNÜŞ DEĞERİ:bellek nbesnesinin HANDLE değeridir;GlobalLock ile handle değerini pointer'a çevirmeliyiz;bu seçenek GMEM_FIXED ile birleşmezGMEM_ZEROINIT0x0040 ;bellek alanını 0 olarak ayırırGPTR0x0040 ;GMEM_FIXED veGMEM_ZEROINIT birleşimiGHND0x0042 ;GMEM_MOVEABLE veGMEM_ZEROINIT birleşimi</span>
dwBytes ayrılacak bellek alanının bayt türünden değeri
Yordam başarılı olduğunda yeni ayrılan bellek alanının HANDLE değerini döndürür.
Yordam başarısız olursa dönüş değeri NULL. Genişletilmiş hata bilgisini GetLastError’dan çekebiliriz.
GlobalLock
GlobalAlloc yordamından dönen değer bir belleğin adresi değil global bellek nesnesinin HANDLE değeriydi. Oysa bize içine birşeyler yazabileceğimiz bir bellek alanı gerekiyor. Bunu yapabilmek içinde bir pointer olması lazım. GlobalLock yordamı GlobalAlloc’tan aldığı HANDLE değerini kullanarak bellek bloğunun ilk baytının adresini döndürür. Bizde o pointer-göstergeci kullanarak işlemlerimizi yapabiliriz.
LPVOID WINAPI GlobalLock( _In_ HGLOBAL hMem );
hMem global bellek nesnesinin HANDLE değeri
Yordam başarılıyla çalıştığında bellek alanının ilk baytının adresini döndürür.
Yordam başarısız olursa NULL döndürür.
GlobalUnlock
GMEM_MOVEABLE olarak ayrılan bellek nesnesinin “lock count”da denilen kilit sayacını azaltır. GMEM_FIXED olarak ayrılan bellek nesnesinde bir etkisi yoktur.
Bellek nesnesinin veri yapısı olarak başlangıçta “lock count” değeri 0 olarak atanır. Hareketli bellek nesneleri için bu değişken GlobalLock ile bir artırılır ve GlobalUnlock ile bir azaltılır.
GMEM_FIXED ile açılan bellek nesneleri her zaman 0 “lock count”a sahiptir.
BOOL WINAPI GlobalUnlock( _In_ HGLOBAL hMem );
hMem global bellek nesnesinin HANDLE değeridir.
Yordam çalıştıktan sonra “lock count” azaltılmasına rağmen kilitli kalmışsa, sıfırdan farklı bir dönüş olur.
Yordam başarılı ise dönen değer 0 olur ve GetlastError NO_ERROR döndürür.
GlobalFree
GlobalAlloc ile yaratılan global bellek nesnesini serbest bırakır ve yaratılan HANDLE değerini geçersiz kılar.
HGLOBAL WINAPI GlobalFree( _In_ HGLOBAL hMem );
hMem global bellek nesnesinin HANDLE değeri. GlobalAlloc veya GlobalReAlloc tarafından yaratılmış olabilir. LocalAlloc ile kullanılması uygun değildir.
Yordam başarılı olmuşsa eğer dönen değer 0’dır.
Yordam başarısız olursa global bellek nesnesinin HANDLE değeri döner.GetLastError ile hata bilgisi döndürülebilir.
LocalAlloc
Heap-Yığıttan belirtilen bayt kadar yer ayırır.
HLOCAL WINAPI LocalAlloc( _In_ UINT uFlags, _In_ SIZE_T uBytes );
uFlags bellek ayırma seçeneklerini içerir:
123456789101112 <span style="font-size: 11px;">LMEM_FIXED0x0000 ;sabit bellek alanı ayırırLMEM_MOVEABLE0x0002 ;hareketli bellek alanı ayırırLMEM_ZEROINIT0x0040 ;bellek alanını 0 olarak başlatırLPTR0x0040 ;LMEM_FIXED ve LMEM_ZEROINIT birleşimidirLHND0x0042 ; LMEM_MOVEABLE ve LMEM_ZEROINIT birleşimidirNONZEROLHND = LMEM_MOVEABLENONZEROLPTR = LMEM_FIXED</span>
uBytes değişkeni ayrılacak baellek alanını büyüklüğünü gösteriyor, bayt türünden
Yordam başarılı olursa yeni yaratılan bellek nesnesinin HANDLE değeri döner
Yordam başarısız olursa NULL döner.
LocalLock
LocalAlloc ile yaratılan yerel bellek nesnesini kititler ve birşeyler yazdırabileceğimiz bellek bloğunun adresini gösteren pointer-göstergeci döndürür.
LPVOID WINAPI LocalLock( _In_ HLOCAL hMem );
hMem LocalAlloc ile yarattığımız bellek nesnesinin HANDLE değeri
Yordam başarılı olursa bellek alanının ilk baytını döndürür.
Başarısız olursa NULL döndürür.
LocalUnlock
LMEM_MOVEABLE olarak ayrılan bellek nesnesinin “lock count”da denilen kilit sayacını azaltır. LMEM_FIXED olarak ayrılan bellek nesnesinde bir etkisi yoktur.
Bellek nesnesinin veri yapısı olarak başlangıçta “lock count” değeri 0 olarak atanır. Hareketli bellek nesneleri için bu değişken GlobalLock ile bir artırılır ve GlobalUnlock ile bir azaltılır.
GMEM_FIXED ile açılan bellek nesneleri her zaman 0 “lock count”a sahiptir.
BOOL WINAPI LocalUnlock( _In_ HLOCAL hMem );
hMem yerel bellek nesnesinin HANDLE değeri
Yordam başarılı olursa sıfır döndürür ve GetlastError çağrılırsa NO_ERROR döndürürlür. “lock count” azaltılmasına rağmen bellek alanı hala kilitli ise, kapatıp açın. Şaka şaka, dönen değer sıfırdan farklı bir sayı olur. Ne olduğunu anlamak için bu durumda GetLastError çağrılıp hata konuna bakılır.
LocalFree
Belirtilen yerel ballek nesnesini serbest bırakır ve HANDLE değerini geçersiz kılar.
HLOCAL WINAPI LocalFree( _In_ HLOCAL hMem );
hMem yerel bellek nesnesinin HANDLE değeri
Yordam başarılı olursa NULL döndürür.
Yordam başarısız olursa global bellek nesnesinin HANDLE değeri döner.GetLastError ile hata bilgisi döndürülebilir.
Şimdi durup Global ve Local yordamları bir karşılaştıralım. 32-bit windowstan sonra bu iki yordam aslında Heap Yordamlarını sarmalayan iki ayrı yordamdır ve çok benzerdir. MSDN’e göre yeni uygulamalar Heap-Yığıt yerdamlarını kullanmalıdır diyor. Ama istisnalar var. Örneğin bazı windows APIleri çalıştırıldıktan sonra “LocalFree” ile bellek alanını serbet bırakılmasını istiyormuş. Dynamic Data Exchange(DDE), clipboard-pano yordamları ve OLE veri nesneleri hala global yordamları kullanıyor. Durum böyle olunca kullandığımız APIlerin belegelendirmelerini MSDN’den okuyup incelemek gerekiyor.
32bit Windows’un Local-Global ayrımı artık yok. 16bit zamanında önemliydi. O nedenle şu an bu seçimi yapmak tamamen kişiseldir. Artık kullandığınız yordam-APIlerin ne istedikleri. Artık 32 bit ile virtual memory – sanal bellek modeli uygulandığı için hem yerel hem global yordamlar 32bit virtual address-sanal adress döndürürler. Artık “far” ve “near” muhabbet yok.
GlobalAlloc LocalAlloc ve HeapAlloc aynı yığıttan bellek ayırırlar. Bellek ayrılamazsa, yani bir hata ile karşılaşıldığında HeapAlloc yordamını buna göre ayarlayabilirken Global ve Local yordamları ayarlayamayız. LocalDiscard ve GlobalDiscard gibi yordamlarda HANDLE’ı geçersiz hala getirmeden etkisizleştirebilir, sonrada LocalReAlloc ve GlobalReAlloc ile yeniden kullanılır hale getirebiliriz ama Heap yordamında bu özellik yoktur.
VirtualAlloc
VirtualAlloc çağıran prosessin “virtual address space – sanal adres uzayı”nda sayfa bölümlerinin durumlarını ayırır(reserve), işler(commit) ya da değiştirir(change). Bu yordam çağrıldığında belirtilen adres uzayı 0 olarak ayrılır. VirtualAlloc bellek alanı ayırırken bazı ek seçenekler daha sunmaktadır. “Page Granularity” kullandığından dolayı bu yordam daha çok bellek kullanımını ortaya çıkartır.
LPVOID WINAPI VirtualAlloc( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect );
lpAddress bu seçeneğe bağlı bir göstergeçtir. Ayrılacak yerin başlangıç adresini gösterir. Genelde NULL girilir. Böylece sistem bize bir adres ayarlar.
dwSize bayt türünde ayrılacak alanın büyüklüğüdür. Bu parametre de NULL ayarlanırsa diğer bellek sayfasının sınırına kadar olan bölge ayrılır.
flAllocationType parametresi ile ayrılacak bellek türünü belirleriz.
12345678 <span style="font-size: 11px;">MEM_COMMIT0x00001000 ;Bu seçenek ile bellek alanı işlenirMEM_RESERVE0x00002000 ;Fiziksel bellekte ayırma işlemi yapmaz ama rezerve ederMEM_RESET0x00080000 ;Ayrılan bellek alanını tekrar kullanmak üzere resetlerMEM_RESET_UNDO0x1000000 ;MEM_RESET başarılı bir şekilde uygulandıysa o işlemi geriye alır</span>
flProtect ile ayrılacak sayfa bölümlerinin nasıl korunacağına dair seçenekler girebiliriz. Ayrıntılı olarak “Bellek Koruma Sabitleri” adresinden bu sabitlere bakabilirsiniz.
1234 <span style="font-size: 11px;"> PAGE_NOACCESSPAGE_GUARDPAGE_NOCACHEPAGE_WRITECOMBINE</span>/blockquote>
Yordam başarılı olursa dönen değer adres olur.
Yordam başarısız olursa NULL döner.VirtualLock
Proses’in sanal adresi uzayını fiziksel bellekğe doğru kitler böylece sonraki erişimlerin bir hata ortaya çıkarmasına izin verilmemiş olur. Ufak bir uygulama yazıyorsanız kullanmanıza gerek olmayabilir.
BOOL WINAPI VirtualLock( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize );lpAddress kitlenecek yerin adresini gösterir
dwSize kitlenecek yerin büyüklüğü
Yordam başarılı olursa sıfır olmayan bir değer döndürür
Yordam başarısız olursa sıfır – 0 döndürür.VirtualUnlock
VirtualLock ile kilitleiğimiz bellek alanı ile işimiz bittiğinde serbest bırakmadan önce kilidi bu yordam ile açıyoruz.
BOOL WINAPI VirtualUnlock( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize );Yordam başarılı olduğunda sıfırdan farklı bir değer döndürüyor.
Yordam başarısız olduğunda dönen değer 0.VirtualFree
VirtualAlloc ile ayırdığımız bellek alanını serbest bırakır ya da reserv duruma getirir. Veya isterseniz her ikisini birden yapar.
BOOL WINAPI VirtualFree( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD dwFreeType );lpAddress ayırdığımız bellek alanının adresi
dwSize büyüklüğü
dwFreeType seçenkleri ayırdığımız bellek alanına ne yapacağımızı söylüyor:
123456 <span style="font-size: 11px;">MEM_DECOMMIT0x4000 ;işlenmiş bellek sayfaların"decommit" eder;serbat bırakmaz rezerv durumda kalır.MEM_RELEASE0x8000 ;bellek sayfaları serbest kalır</span>Yordam başarılı olursa eğer sıfırdan farklı bir değer döner
Yordam başarısız olursa 0 döner.CoTaskMemAlloc
Windows’ta “COM-aware”, COM nesleri farkındalığı olan yordamlar var. Genelde bu apiler “Co” ön eki ile kendilerini belli ederler. Özellikle C++’da COM nesneleri ile çalışıyor ve ayırdığınız bellek alanlarının açıkça COM nesneleri ile ilişkilendiriyorsanız bu tür yordamlara gereksiniminiz var.
IMalloc::Alloc yordamıyla aynı şekilde bellekte yer ayırır.LPVOID CoTaskMemAlloc( _In_ SIZE_T cb );cb bayt türünde ayrılacak bellek bloğu
Eğer yordam başarılı olursa ayrılan bellek bloğunu döndürür.CoTaskMemFree
CoTaskMemAlloc yordamı tarafından döndürülen bellek alanını serbest bırakır.
void CoTaskMemFree( _In_opt_ LPVOID pv );pv serbest bırakılacak bellek alanının göstergeci(pointer)
Geri döndürdüğü bir değer yoktur.malloc
malloc çalışma zamanı bağımlısı olmasına rağmen derleyici ve dilden bağımsızdır. Genelde “portable” taşınabilir olarak ifade edilen bir fonksiyondur. GlobalAlloc ise aksine Windows APIsidir.
void *malloc( size_t size );size bayt türünden ayrılacak bellek bloğunun büyüklüğü
Yordam başarılı olduğunda bellek bloğunun adresini döndürür. yetersiz alan olduğunda ise 0 döndürür.
Yordam başarısız olursa çarşı karışır. errno gibi global makoları kurar. Mesela – ENOMEM Not enough memory – gibi.free
malloc ile ayırdığımız bellek bloğunu serbest bırakmaya yarar.
void free( void *memblock );memblock malloc ile ayırdığımız yeri gösteren göstergeç-pointer
Bu konuya başladığımda yazının bu kadar uzayacağını öngörmemiştim. Evet çok önemli ve derin bir konu olduğunu biliyorum ancak bazı ayrıntılarda öylesine geçilmiyor. Derinlemesin öğrenmek herzaman daha akıllıca bir seçim.
[dm]149[/dm]
Comments of this post