4 Nisan 2012 Çarşamba

Virtual Memory nedir? - II - free()

alloc() sülalesi komutların tersi olan free() komutunu kullandığımızda memory managera aldığımız alanların boşa çıktığını söyleriz. Bu durumda Windows ile Linux'un davranışları biraz farklılık gösteriyor. Örnek için aşağıdaki kodu kullanabiliriz. Derlediğimizde program karakter girişi aldığında 1024 pagelik bir alanı doldurur, tekrar karakter girişi aldığında boşaltır.
#define PAGECOUNT 1024 //1024 tane 1024 int dolduracagiz = 1024 sayfa
#define LISTCOUNT 1024

int j=0;
int i=0;
char c;
int *chunklist[PAGECOUNT]; //int 4 byte oldugundan 1024 sayfalik alan
//bu pointer dizisinin kendisi de stackte 1 sayfa tutuyor

//lowmem pointerlardan higmem pointerlara dogru gidiyoruz.
//heap grows upwards.  
printf("ENTER'a basinca 1024 page doldurulacak.\n");
getchar();
while(i<PAGECOUNT) {
 chunklist[i]=(int *) calloc(1024,sizeof(int)); //1 page yer ayır
 for(j=0;j<LISTCOUNT;j++) { // ayrilan alan içinde 1 page boyutunda int array oluştur.
  int *p = chunklist[i];
  p[j]=j;
 }
 i++;
}

printf("Toplam 1024 page dolduruldu.\n1024 page bosaltmak icin ENTER'a basin...\n",i);
getchar();

while(i>1) {
 free(chunklist[--i]);
}
printf("Tum sayfalar bosaltildi.\nCikmak icin ENTER'a basin\n");
getchar();
Programı derleyip Linux altında çalıştırdığımızı düşünelim. İlk açtığımızda tuşa basmadan PID'sini öğrenip
cat /proc/$PID/status | grep VmData
dersek heapin kullandığı alanı görebiliriz. Bellek talep edip doldurduktan ve boşalttıktan sonra da processin status bilgisine baktığımızda 4 MB civarı alanın dolduğunu ve boşaldığını görebiliriz. Bu sayı her zaman page boyutuna bölünebilir, page boyutuna bölersek processin bu iş için kullandığı page sayısına erişiriz. (Doğrudan sayfa sayılarını görmek isterseniz /proc/$PID/statm'e bakın.)



Şimdi son while döngüsünü aşağıdaki kod ile değiştirelim. Böylece en son aldığımız sayfayı boşaltmayacağız.
i=0;
while(i<1023) {
 free(chunklist[i++]);
}
Kullanılan sayfa miktarına baktığımızda azalmadığını göreceğiz. Bunun nedeni Linux'un free() kullanıldığında yalnızca heapin üstündeki alanları boşaltması, aradaki sayfaları boş olmalarına rağmen processten almamasıdır. Tabii ki memory yetersizliği durumunda kswapd kernel threadi boş sayfaları geri alır ama sonuçta bir processin /proc'tan bakınca göründüğünden daha fazla boş alanı olabilir. (yani /proc/$PID/ içindeki bilgileri kullanan monitoring uygulamaları (örneğin ps) boşaltılmış fakat arada kalmış sayfaları bilemeyeceğinden processin kullandığı belleği değil processin sahip olduğu belleği gösterir. Ama örneğin free komutu doğrudan /proc/sys/vm ve /proc/meminfo kullandığından sistemdeki gerçek boş sayfa miktarını gösterecektir.)

Bunu denemek için sayfaları boşalttıktan sonra programdan çıkmadan memory yetersizliği ortaya çıkaralım. Aşağıdaki kod memory'i 400MBlık bloklar halinde sorumsuzca doldurur. Programdan çıkmak için Ctrl+D'ye basabilirsiniz.
int i=0;
int yuzmb = 1024*1024*100; //yuz mb degil yuz mb tane
char c;

printf("ENTER'a bastikca 400MB doldurulacak...\n");
while(c=getchar()!=EOF) {
 //400 mb doldurur
 void *chunklist = calloc(yuzmb,sizeof(int));
 for(i=0;i<yuzmb;i++) {
  int *fill=(int *) chunklist;
  fill[i]=i;
 }
 i++;
 printf("toplam %d MB allocated\n", i*100*4);
}
Programı çalıştırmadan önce
swapoff -a
demek isteyebilirsiniz, harddiski doldurmak oldukça zaman alıyor, boşuna beklemeyin.

Programı çalıştırıp memory doldurdukça diğer programın kullandığı sayfa sayısının azaldığını göreceksiniz.
(Not: 32bit sistemlerde 3GB'tan fazla memory kullanamazsınız. Not2: Memory'i dibine kadar doldurayım derken işletim sistemini de göçertebilirsiniz, normalde işletim sistemleri bunun önlemini alır ama ne olur ne olmaz kaydedilmemiş dosya filan bırakmayın)

İlk programı Windows altında çalıştırıp Task Manager ile izlediğimizde iki versiyonda da kullandığı memory miktarının free() döngüsünden sonra azaldığını görürüz. Windows processin boşalttığı sayfaları yerlerine bakmadan geri alır. Ama dikkat ederseniz (ve bol bol boş RAMiniz varsa) yine de processte haddinden fazla memory kaldığını görürsünüz.

Yine Windows'ta da memory sıkıntısı yaratırsanız memory managerın tüm processlerden boş sayfaları azar azar geri aldığını izleyebilirsiniz. (çok sıkıntı yaratırsanız dolu sayfaları da alıp paging yaptığını görebilirsiniz, pagingden 4. yazıda bahsedeceğim.)

Son olarak daha önce boşalttığınız bir alanı tekrar bırakmaya çalıştığınızda da segfault yiyebilirsiniz. Yemeyebilirsiniz de, compiler ve işletim sistemi ayarlarına göre değişkenlik gösterir.

Memory leak ve page fragmentation konularına değinmedim, belki başka bir yazıda bahsederim. Sonraki iki yazıda işin programlama değil de sistem adminliği tarafını ilgilendiren konulardan bahsediyor olacağım.

1 yorum: