Genelde C kütüphanesi (libc) kullanmanın tek yol olduğu ve doğrudan sistem çağrıları yapmanın kötü olduğu söylenir. Bu doğrudur. Bir bakıma... Genel olarak, libc kütüphanesinin kutsal olmadığını bilmelisiniz ve pekçok durumda sadece bazı denetimler yapar, sonra çekirdeğe çağrı yapar ve ardından errno'ya atama yapar. Bunu kendi programınızda da yapabilirsiniz (eğer ihtiyacınız varsa) ve programınız bir düzine kat daha küçük olacaktır, bu da gelişmiş bir başarım artışına sebep olacaktır, bu da sırf paylaşımlı kütüphaneleri kullanmadığınızdan kaynaklanacaktır (durağan (static) kütüphaneler daha hızlıdır). Sembolik makina dili ile programlamada libc kullanımı pratik birşeyden çok zevk/inanış meselesidir. Linux'un POSIX standartlarına uygun olmayı hedeflediğini unutmayın, benzer şekilde libc de. Bu da, hemen her libc "sistem çağrısı" sözdizimin gerçek çekirdek sistemi çağrılarındaki sözdizimiyle örtüşmesi anlamına gelir (ve tam tersi). Buna ek olarak, GNU libc (glibc) sürümden sürüme daha yavaş hale gelmekte ve daha çok bellek tüketmektedir. İlerde siz de kendi, değişik türlerde, libc'ye özel işlevlerinizi (sadece birer sistem çağrısı değil) tanımlayacaksınız (printf() ve şürekası)... Ve buna hazırsınız, değil mi? :)
Doğrudan sistem çağrıları yapmanın artı ve eksileri şu şekilde özetlenebilir:
- olası en küçük boyut; son baytı sistem dışında bırakmak
- olası en yüksek hız; favori karşılaştırmalı değerlendirme (benchmark) dışı döngüleri bunun dışında bırakmak
- tam denetim: program/kütüphanenizi size özgü dile veya bellek gereksinimlerine veya herhangi bir şeye uydurabilirsiniz.
- libc çerçöplerinin yol açacağı bir kirlilik olmaz
- C çağrı uzlaşımlarının yol açacağı bir kirlilik olmaz (eğer kendi dilinizi veya ortamınızı tasarlıyorsanız)
- durağan kütüphaneler libc yükseltmelerinden ve çökmelerinden veya yorumlayıcıya #! yolu ile asılmanızdan sizi bağımsız kılar. (ve daha hızlıdır)
- biraz da eğlence içindir (sembolik makina dili dışında heyecanlanmaz mısınız?)
Eğer bilgisayarınızda bir başka program da libc kullanıyorsa, libc kodunun yinelenmesi otomatik olarak belleğin, korunması yerine, boşa harcanmasına sebep olacaktır.
Pekçok durağan ikilikte (static binary) gereksiz yere tanımlanan servisler bellek israfıdır. Fakat kendi libc yerdeğiştirmenizin bir paylaşımlı kütüphane olmasını sağlayabilirsiniz. (NBB: Bu yukarıdaki iddiasını yalanlamıyor mu? ;-))
Herşeyi sembolik makina dili ile yazmak yerine, bir çeşit bayt kodu, sözcük kodu veya yapısal yorumlayıcıya sahip olmakla, boyut çok daha iyi korunur. (derleyicinin kendisi C veya sembolik makina dilinde yazılabilir). İkilik çeşitliliğini küçük tutmanın en iyi yolu, çoklu ikilikler yapmamaktır, yerine #! önekiyle başlayan yorumlanan işlem dosyaları kullanmaktır. Bu, OCaml'ın sözcük kodu kipinde çalıştığındaki durumdur (eniyilenmiş doğal kod kipine karşın) ve de libc kullanımıyla uyumludur. Bu aynı zamanda unix araçlarının yeniden gerçeklenimi olan Tom Christiansen'in Perl Güç Araçları (Perl PowerTools)'nın nasıl çalıştığının açıklamasıdır. Son olarak, bunları küçük tutmanın bir yolu da, tam olarak yolu kodlanmış harici bir dosyaya bağımlı olmamaktır, bu da kütüphane veya yorumlayıcı olsun, tek bir ikilik dosyaya sahip olmak ve buna sabit veya sembolik bağlar yapmaktır: aynı ikilik size en makul alanda, alt yordamların gereksiz kullanımı veya gereksiz ikilik başlıkları olmadan, ihtiyacınız olan herşeyi sunacaktır; kendine özel davranışı argv[0] değerine bakarak yönlendirecektir; bu durumda tanınan ismiyle çağrılmaz, bir kabuğa öntanımlı olabilir ve muhtemelen bir yorumlayıcı olarak da kullanışlı olmuş olur!
Nadir linux sistem çağrıları yanında libc'nin sunduğu pekçok işlevsellikten faydalanamazsınız: malloc, thread, locale, password, yüksek-seviyeli ağ yönetimi, v.b. işlevsellikler, kılavuz sayfalarının 2. bölümünde değil, 3. bölümünde yer alır.
Bu yüzden, libc'nin,
printf()'den
malloc() ve
gethostbyname'e uzanan çok sayıda parçalasını yeniden gerçeklemek zorunda kalabilirsiniz. libc varken bu gereksizdir, hatta
oldukça sıkıcı olabilir. Bazılarının libc'nin bazı kısımları için "hafif" (ligth) yerdeğiştirmeler yazdıklalarına dikkat ediniz - bunları inceleyiniz! (Redhat'in minilibc'si, Rick Hohensee'nin
libsys'si, Felix von Leitner'in
dietlibc'si, Christian Fowelin'in
libASM'si,
asmutils projesi de tamamen saf sembolik makina libc'si ile çalışmaktadır)
Durağan kütüphaneler sizi, libc güncellemelerinden ve aynı zamanda, gzip-sıkıştırılmış dosyalarını düzgün şekilde ihtiyaç olduğunca açmanızı sağlayan, zlib paketi gibi libc eklentilerinden faydalanmaktan alıkoyar.
libc tarafından eklenen çok az sayıdaki komutun sistem çağrılarının maliyetine kıyasla küçük bir hız yükü olabilir. Eğer hız gözönüne alınırsa, temel sorununuz, onların sarmalayıcı işlevlerini değil, kendi sistem çağrılarınızın kullanımıdır.
Kendi çağrı uzlaşımları olan ve standart uzlaşım kullanımında kural dönüşüm yüküne (high convention-translation overhead) büyük önem veren L4Linux gibi Linux'un mikro çekirdek sürümleri çalıştırılırken, sistem çağrıları için standart sembolik makina dili API'leri kullanmak, libc API'leri kullanmaktan daha yavaştır (L4Linux, sistem çağrı API'leri ile yeniden derlenmiş şekilde gelir; elbette kendi kodunuzu onun API'leriyle tekrar derleyebilirsiniz).
Genel hız eniyileme konularıyla ilgili olarak önceki konulara bakınız.
Eğer sistem çağrıları size göre çok yavaşsa, kullanıcı adasında kalmak yerine çekirdek kaynak kodlarını (C dilindeki) elden geçirmeyi isteyebilirsiniz.
Eğer yukarıdaki artı ve eksileri zihninizde iyice ölçüp tarttıysanız ve hala doğrudan sistem çağrılarını kullanmak istiyorsanız, size bazı önerilerim olacak:
Sistem çağrı işlevlerinizi taşınabilir bir şekilde, C dilinde (sembolik makina dilinin taşınamaz özelliğine karşın) asm/unistd.h ile sunulan makroları kullanarak tanımlayabilirsiniz.
Sistem çağrısı işlevlerini değiştirmeyi deneyecekseniz, libc'den kaynak kodunu alıp inceleyin. (Ve daha iyisini yapabileceğinizi düşünüyorsanız, bunu yazarlara bildirin!)
İstediğiniz her işi yapan bir sembolik makina kodu için
Özkaynaklarına bakınız.
Temelde,
eax içerisine
__NR_sistemçağrı_ismi numarası (
asm/unistd.h dosyasındadırlar) ile
int 0x80 değeri ve parametreleri de sırasıyla (
altıya kadar)
ebx,
ecx,
edx,
esi,
edi,
ebp içine konur.
Sonuç eax içerisinde döndürülür, negatif sonuçlarda hata ile döner, bunun karşılığı da libc içerisinde errno'dur. Kullanıcı yığıtına dokunulmaz, dolayısiyle bir sistem çağrısı yaparken geçerli bir kullanıcı yığıtına ihtiyacınız yoktur.
| Not |
---|
ebp'ye 6 parametre aktarımı Linux 2.4 sürümünde mümkün olmuştur, daha önceki Linux sürümleri yazmaçlarda sadece 5 parametreye bakıyordu.
|
Başlatırken bir sürece parametrelerin aktarılmasında olduğu gibi, genel prensip, yığıtın orjinal olarak argüman sayısını (
argc) ve ardından
*argv'ler halinde argüman göstericilerini, bundan sonra da
environ (ortam) için boş gösterici ile sonlandırılmış boş karakter sonlandırmalı
isim=deger dizgelerini
içereceğidir. Daha ayrıntılı bilgi için,
Özkaynaklar bölümünü okuyunuz, libc'nizdeki C başlatma (
crt0.S veya
crt1.S) kodlarını veya bunların Linux çekirdeğinde olanlarını (
exec.c ve
linux/fs içindeki
binfmt_*.c) inceleyiniz.