Flexible Arrays — Linux Kernel Part2
Flexible Arrays’ı iki part halinde inceleyeceğiz.
İlk part: Flexible Arrays — Linux Kernel Part1 Makalesine buradan ulaşabilirsiniz.
Bu part’da ise Flexible Arrays’ın linux kernel içindeki kullanımını inceleyeceğiz.
Android Kernel “flex_array.c” dosyası.
Linux Kernel “flex_array.c” dosyası.
Flexible Array - Linux Kernel
Büyük boyutlu bitişik bellek tahsisleri Linux kernel’de güvenilmez olabilir.Kernel programcıları bu soruna “vmalloc()” kullanarak üstesinden gelirler. Ancak bu çözüm ideal bir çözüm değildir.
32 bit sistemlerde,”vmalloc()”’dan gelen bellek nispeten küçük bir adres alanına eşlenmelidir.SMP(Shared-Memory Multiprocessing) sistemlerinde “vmalloc()” tahsislerinin gerektirdiği sayfa tablosu değişiklikleri tüm CPU’larda yüksek maliyetli işlemciler arası kesintiler gerektirebilir. Genel olarak tüm sistemler üzerindeki etkisine bakarsak,”vmalloc()” TLB(Translation Lookaside Buffer) üzerindeki baskıyı artırır ve sistemin performansının düşmesine sebep olur.
NOT: TLB(Translation Lookaside Buffer),bir kullanıcının bellek konumuna erişmesi sırasında gerekli olan süreyi azaltmak için kullanılan bir bellek önbelleğidir ve Çip’in bellek yönetim biriminin bir parçasıdır. TLB,sanal(virtual) belleğin son çevirilerini fiziksel belleğe depolar.Dolayısıyla “vmalloc()” ile muhatap haldedir.
Çoğu durumda “vmalloc()”’tan gelen bellek ihtiyacı bir diziyi daha küçük parçalardan bir araya getirererk ortadan kaldırabilir. Dolayısıyla “Flexible Array” bu görevi kolaylaştırmak için mevcuttur.
Flexible Array isteğe bağlı olarak(sınırlar dahilinde) sabit boyutlu nesneleri tutar. Bu nesnelere erişmek için ise bir tam sayı indeks değeri kullanır.Bu yapı ile bellek tahsis hataları nispeten nadirdir.Ancak dizilerin doğrudan dizine eklenememesi,nesne boyutunun sistem sayfa boyutunu aşamaması,ve verileri diziye yerleştirmek için bir kopyalama işlemi gerektirmesi bu yapının olumsuz yönleri olarak görülebilir.Ayrıca bu yapının hiçbir dahili kilitleme(lock) yapmadığını da belirtmekte fayda var. Dolayısıyla bir diziye eş zamanlı erişim durumunda,diziyi çağıranın bunun önlemini uygun şekilde alması gerekir.
Flexible Array kullanımı aşağıda verilmiştir:
#include <linux/flex_array.h>
struct flex_array *flex_array_alloc(int element_size,
unsigned int total,
gfp_t flags);
Fonksiyonun parametrelerini incelersek:
element_size //Dizide nesne(item) olarak yer alacak nesnenin boyutu.
total //Dizide tutulacak maksimum nesne sayısı.
flags //Dizi için kullanılacak bellek ayırma bayrakları.
Flags olarak kullanılacak argümanlar iyi seçilmelidir. Yanlış,yerinde olmayan bir “flags” kullanımı hoş olmayan yan etkilere yol açacaktır. Özellikle yüksek bellek talep için ayrılan “flags” argümanları yan etkilerin riskini arttırır.
Ayrıca Flexible Array’ı derleme zamanında aşağıdaki şekilde tanımlamak da mümkündür:
DEFINE_FLEX_ARRAY(name, element_size, total);
Verilen makro ilk parametre olarak “name” alıyor.Bu parametreyle bir dizi ismi belirlemelisiniz. Diğerleri ise yukarıda açıklandığı şeklindedir. Makroda belirlediğiniz parametreler derleme zamanında geçerlilik açısından kontrol edilecektir.
Verileri bir Flexible Array’da depolamak için aşağıdaki fonksiyon kullanılabilir:
int flex_array_put(struct flex_array *array, unsigned int element_nr,void *src, gfp_t flags);
Yukarıdaki fonksiyon çağırısı ile “src”’deki verileri “element_nr” ile gösterilen konumda “array”’a kopyalayacaktır. Burada dikkat edilmesi gereken nokta,”element_nr” değerinin dizi oluşturulduğunda belirtilen maksimum değerden küçük olması gerektiğidir. Bellek tahsisi yapılması gerekiyorsa “flags” kullanılmalıdır.İşlem başarılı ise return değeri “0”,aksi halde negatif bir hata kodu olacaktır.
Bir tür atomik kaynakla çalışırken verileri bir flexible array’da saklama ihtiyacı olabilir. Bu durumda bellek ayırıcıda sleep durumunda kalmak kötü bir sonuç doğrur. Bunu önlemek için flags değeri olarak “GFP_ATOMIC” kullanılabilir. Ancak bunun için daha iyi bir yol vardır.Dolayısıyla atomik kaynağa(context) girmeden önce gerekli tüm bellek tahsislerinin yapılmasını sağlamak en iyi yoldur. Bunun için “flex_array_prealloc()” fonksiyonu kullanılabilir. Bu fonksiyona aşağıda yer verilmiştir.
int flex_array_prealloc(struct flex_array *array, unsigned int start,unsigned int nr_elements, gfp_t flags);
Bu fonksiyon “start” ve “nr_elements” ile tanımlanan aralıkta indeklenen elemanlar için bellek tahsisini sağlayacaktır.Bundan sonra,o aralıktaki bir öğe üzerindeki bir “flex_array_put()” fonksiyon çağrısının engellenmemesi garanti edilir.
Verileri diziden geri almak “flex_array_get()” ile yapılır.Fonksiyonun dönüş değeri veri öğesini gösteren bir gösterici(pointer) veya NULL olacaktır.
void *flex_array_get(struct flex_array *fa, unsigned int element_nr);
Dizideki öğeler “flex_array_clear()” ile temizlenebilir.
int flex_array_clear(struct flex_array *array, unsigned int element_nr);
Bu fonksiyon verilen öğeyi “FLEX_ARRAY_FREE” olarak ayarlar ve sıfır(0) döndürür. Belirtilen öğe için depolama tahsis edilmemişse,”flex_array_clear()” sıfır yerine “-EINVAL” değerini döndürür.
Bir öğeyi temizlemenin,onunla ilişkili depolama alanının serbest bırakılmadığını unutmayın.Bir dizinin ayrılan boyutunu azaltmak-küçültmek için “flex_array_shrink()” fonksiyonu kullanın.
int flex_array_shrink(struct flex_array *array);
Bu fonksiyonun döndüreceği değer gerçekte serbest bırakılan bellek sayfalarının sayısı olacaktır.
Bir diziye ait tüm öğeleri kaldırmak “flex_array_free_parts()” ile mümkündür.
void flex_array_free_parts(struct flex_array *array);
Bu fonksiyon çağrısı tüm dizi öğelerini serbest bırakır,ancak dizinin kendisine dokunmaz.Tüm dizinin serbest bırakılması “flex_array_free()” ile yapılır.
void flex_array_free(struct flex_array *array);
Flexible Array Fonksiyonlar
Flexible array oluşturmak için:
struct flex_array * flex_array_alloc(int element_size, unsigned int total, gfp_t flags)
Parametreleri:
int element_size //Eleman nesne boyutu.
unisgned int total //Depolanacak maksimum nesne sayısı.
gfp_t flags //GFP bayrakları.
Return: “flex_array” struct nesnesi döndürür.
"start” ve “nr_elements” ile tanımlanan aralıkta indekslenen öğeler için belleğin ayrılmasını/tahsisini sağlar:
int flex_array_prealloc(struct flex_array * fa, unsigned int start, unsigned int nr_elements, gfp_t flags)
Parametreleri:
struct flex_array * fa //Bellek tahsis edilecek dizi.
unsigned int start //Adress başlantıç.
unsigned int nr_elements //Eleman sayısı.
gfp_t flags //GFP bayrakları.
Flexible Array tüm öğelerini kaldırır:
void flex_array_free(struct flex_array * fa)
Parametreleri:
struct flex_array * fa //Serbest bırakılacak dizi.
Flexible array’ın tüm öğelerini kaldırır ancak dizinin kendisini yerinde bırakır.Yani ilgili dizi için kullanılan bellek yerini-varlığını kurumaya devam eder:
void flex_array_free_parts(struct flex_array * fa)
Parametreleri:
struct flex_array * fa //Boşaltılacak dizi.
Verileri bir Flexible array’da depolar:
int flex_array_put(struct flex_array * fa, unsigned int element_nr, void * src, gfp_t flags)
Parametreleri:
struct flex_array * fa //Elemanların saklanacağı dizi.
unsigned int element_nr //Kopyalanacak konum.Dizi oluşturulduğunda belirtilen maksimum değerden az olmalıdır.void * src //Diziye kopyalanacak veri kaynağı.
gfp_t flags //GFP bayrakları
Return: Başarılı durumda sıfır(0),aksi durumda negatif bir hata kodu döndürür.
Dizide belirtilen eleman/elemanları temizler:
int flex_array_clear(struct flex_array * fa, unsigned int element_nr)
Parametreleri:
struct flex_array * fa //Temizlenecek öğelerin ait olduğu dizi.
unsigned int element_nr //Temizlemek için öğre konumu.
Return: Başarılı durumda sıfır(0),aksi durumda “-EINVAL” döndürür.
Verileri bir flexible array’a alır-çeker:
void * flex_array_get(struct flex_array * fa, unsigned int element_nr)
Parametreleri:
struct flex_array * fa //Verilerin alınacağı-çekileceği dizi.
unsigned int element_nr //Verilerin alınacağı-çekileceği öğe konumu.
Return: Veri öğresine işaret eden bir pointer,ya da NULL döndürür.
Bir dizi için ayrılan alanın boyutunu küçültür:
int flex_array_shrink(struct flex_array * fa)
Parametreleri:
struct flex_array * fa //Küçültmek için dizi.
Return: Küçültme sonrasında gerçekte serbest bırakılan bellek sayfalarının sayısını verir.
KAYNAKLAR:
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Zero-Length.html
https://www.kernel.org/doc/html/v4.14/core-api/flexible-arrays.html
https://elixir.bootlin.com
https://android.googlesource.com
https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html
-end of