1.- Bir program üretme süreci. Giriş.
Bugünlerde geliştirme çevrelerinde bir program üretme süreci,
programcıların ve tasarımcıların, alışkanlık ve deneyimlerde
yaşadıkları, evrimin meyvesidir.
Bu süreç aşağıdaki adımlardan oluşur:
Kaynakyazılımın bir metin dosyadüzenleyicisi yardımıyla
yüksek düzeyli bir dilde yaratımı. Çok büyük programların
işlenmeleri, tek bir dosya içinde tutulmak istenmeleri durumunda,
oldukça zordur. Bu nedenle, kaynakyazılım, birkaç kaynakyazılımdan
oluşturulmuş işlevsel modüllere bölünürler. Bu modüllerdeki
kaynakyazılımın, bazı dillerin belirgin bir görevi diğerlerinden
daha iyi yapabilmeleri nedeniyle, aynı dilde yazılma zorunluluğu
yoktur.
Program için kaynakyazılım dosyalarının, yaratılımdan sonra,
nesnel düzgü (object code) olarak adlandırılabilen ve
makinenin yerine getirebileceği, düzgü kesimlerine (segment of code)
dönüştürülmesi gerekir. Bu düzgü (kod), makinenin doğrudan yerine
getirebileceği güdümlerde yazılmış olmakla birlikte kaynakyazılımla
aynı işlemleri yerine getirir.
Kaynakyazılımın nesnel düzgüye dönüştürülme süreci derleme
(compilation) olarak bilinir.
Derleme birimler tarafından gerçekleştirilir ve bir derleme oturumu,
derleyiciye bağlı olarak, programın bir parçasını ve genelde yalnızca
bir ya da birkaç dosyayı içerir.
Derlenmiş nesnel düzgü, henüz dönüştürülmüş olan programın genel
parçası içindeki program, altyordam, değişkenleri içerir ve bir
sonraki aşamaya aktarır.
Program için makine dilindeki dosyaların tümünün üretilmesinden
sonra bunların bağlayıcı adı verilen özel bir olanak
aracılığıyla birleştirilmesine yönlenilir.
Bu süreçte, bir modülde kullanülan güdümlerle bir başka modülden
çağrılan (altyordam çağrıları ya da başka modüllere ait olan
ya da başka modüllerde tanımlanan değişkenlere yapılan göndermeler
gibi) tüm işlevler "bütünselleştirilir.
Böylece oluşan yapı normal olarak doğrudan yüklenebilen ve
koşulabilen bir programdır.
Bir programın çalıştırılması işletim sisteminin önemli bir
parçası olan bir yazılım parçası tarafından gerçekleştirilir. Bu
yazılım parçası, Linux durumunda exec() sistem çağrı
fonksiyonudur.
Bu fonksiyon dosyayı bulur, süreçlere bellek ataması yapar,
dosya içeriğinin ( güdümleri ve değişkenlerin baçlangıç değerlerini
içeren ) özel parçalarını yükler ve programda 'metin' (text) olarak
genellikle çalıştırılabilen dosyanın kendisinde gösterilen
noktada denetimi MİB (Merkezi İşlem Birimi)'ne aktarır.
2.- Program üretim süreçlerinin kısa tarihçesi.
Program üretim süreçleri, daima en etkin çalıştırılabilen
programa erişmek ya da sistem kaynaklarını en iyi biçimde kullanmak
için sürekli bir evrim geçirmektedir.
Başlangıçta, programlar doğrudan makine dilinde yazılırdı.
Sonraları, daha yüksek düzeyli bir dilde program yazma ve yazılan
programın makine diline çevrilmesinin, çevirme'nin sistematik
doğası nedeniyle, özdevimlileştirilebileceği (otomatikleştirilebileceği)
anlaşılmıştır. Bu durum, yazılım üretkenliğini arttırmıştır.
Programların derlenebilmesinin gündeme gelmesiyle ( Burada
derlemenin evrimini çok basitleştirmiş bulunmaktayım, gerçekte
bugünlere gelinebilmesi, derleme olayının çok karmaşık bir süreç
olmasından dolayı, atılması çok güç gerçekleştirilebilmiş bir
adımdı ), program üretim süreci program güdümlerinin içerildiği
bir kaynak dosyasının yaratılması, onun derlenmesi ve son adım
olarak da çalıştırılması aşamalarından oluşur duruma gelmiştir.
FORTRAN kullanımıyla görüldüğü üzere, kısa bir süre sonra,
derleme sürecinin ederi çok yüksek olduğu ve çok fazla kaynak
kullanımı ve MİB (Merkezi İşlem Birimi) gerektirdiği ve bu
programlarda içerilen birçok fonksiyonun çeşitli programlarda
yinelenerek defalarca kullanıldığı ortaya çıkarılmıştır.
Üstelik, birilerinin programlarında değişiklik yapmaları
durumunda, derleme, değişiklik yapılan kesimi derlemek için
tüm kaynak yazılımın yeniden makine diline çevrilmek üzere
derleyiciye verileceği anlamına gelmekteydi.
Bu durum, modüllerle derlemenin gündeme getirilmesinin
nedeni olmuştur.
Bu süreç, anaprogramın bir yana, sık sık defalarca kullanılan
ve önceden derlenip ( bir kitaplık öncüsü gibi nitelendirebileceğimiz )
özel bir yerde belgeliklenmiş (arşivlenmiş) fonsiyonları bir yana
ayrılması temeline dayanır.
Fazladan çaba göstererek programlarını defalarca devreye
sokmaksızın programlar da yazılabilirdi. O zaman bile, programın
bağlanma sürecinde tüm parçaların birleştirilmesinin gerekliliği
ve bu işlevin programcı tarafından yerine getirilme zorunluluğunun
ortaya çıkması nedeniyle, süreç karmaşık olacaktı ( Parça
birleştirmedeki sıkıntı, bilinen bir fonksiyonun kullanılması
durumunda bilinmeyen bir fonksiyonun devreye sokulma gereksiniminin
ortaya çıkabilmesidir )
3.- Kitaplık Nedir?
Yukarıda sözü edilen sorun Kitaplıkların yaratılmasına yolaçmıştır.
Kitaplık, özel bir tür dosyadan başka birşey değildir. Daha açık
konuşmak gerekirse, onun tar(1) ya da cpio(1)
türü bir belgelik (arşiv) olduğunu söyleyebiliriz. Bu dosya,
bağlayıcının dosya biçimini anlayabileceği özgün nitelikler içerir.
Kitaplık belgeliği (arşivi) belirtildiğinde BAĞLAYICI YALNIZCA
PROGRAMIN GEREKTİRDİĞİ MODÜLLERİ SEÇER, bunların dışında başka
bir şeyi devreye sokmaz.
Yeni gündeme gelen bir başka yarar, artık büyük fonksiyon
kitaplıklarının kullanılabilmesi ve programlayıcının kitaplık
içindeki tüm fonksiyonlararası ilişkileri bilmek zorunda
kalmaktan kurtarılması olmuştur.
Sözünü etmekte olduğumuz kitaplık bundan daha fazla
gelişmiş değildir. O, yalnızca, belgeliğin (arşivin) başlangıcında
yeralan yeni özel bir dosyayla donatılmıştır. Bu dosya modüllerin ve
kimliklendiricilerin betimlendiği bir yapı taşır. Bu dosya
yardımıyla, bağlayıcı kitaplığın tümünü okumak zorunda
kalmaksızın fonksiyon çözümlemesini gerçekleştirir. Böylece
kitaplığın defalarca okunmasına gerek kalmamış olur.
Bu, simge çizelgelerinin kitaplık belgeliğine (arşivine) eklenmesi
süreci Linux'ta ranlib(1) güdümü tarafından gerçekleştirilir. Böylece
tanımlanan kitaplıklar DURAĞAN KİTAPLIKLAR olarak bilinirler.
İlk çokgörevli sistemlerin devreye sokulmasıyla yeni bir
gelişme, yazılım paylaşımı gündeme getirilmiştir.
Aynı sistemde, aynı yazılımın iki kopyası birden devreye
sokulduğunda iki sürecin aynı yazılımı paylaşması ilginç
gözükmüştür. Çünkü, bunun gerçekleşmesi durumunda, bir programın
kendi kendini değiştirmemesinden dolayı bellekte çok sayıda
kopyanın tutulmasına gereksinim kalmayacağı düşünülmüştür.
Bu düşünce çok kullanıcılı dev sistemlerde büyük tutarlarda
bellek korunumuna olanak sağlamıştır.
Bu yeniliğin bir adım ötesi olarak birisi (kim olduğunu
bilmiyorum ama düşünce gerçekten büyüktü ;-) birçok programın
kendileri farklı olmakla birlikte aynı kitaplığı kullandıklarını
düşünmüştür. Bu yapıda bir programın kullandığı kitaplık kesimiyle
bir diğerinin kullandığı kitaplık kesimi aynı olmak zorunda
değildi.
Üstelik anayazılım da aynı değildi (farklı programlar), dolayısıyla,
onların metinleri de paylaşılmamaktaydı. Bu durumda, sözü edilen
birey, aynı kitaplığı kullanan farklı programlar böyle bir kitaplık
paylaşımına girebilirse, bellek kullanımı azaltılabilecekti.
Bugün, artık, farklı programlar, özdeş program metnine sahip
olmaksızın kitaplık yazılımlarını paylaşabilmektedirler.
Bununla beraber, artık, süreç daha da karmaşıklaşmıştır.
Çalıştırılabilir program artık bütünüyle bir bağlantı olmayıp
kitaplık kimliklendiricilerine başvurma, program yükleme sürecine
ertelenmektedir.
Bağlayıcı (Linux durumunda ld(1)) paylaşımlı bir kitaplık kullanımının
söz konusu olduğunu algılayıp programa kendi düzgüsünü (kodunu)
yerleştirmez. Sistemin kendisi, yani çekirdek, exec() programını
devreye sokarken paylaşımlı kitaplık kullanan bir yazılımın devreye
sokulduğunu algılar (Bu işi, paylaşımlı kitaplığı kendi metnine
atayarak, kitaplık değerleri için özel bellek ataması yaparak, vb..
gerçekleştirir.) Çalıştırılabilir bir dosyanın yüklenmesi durumunda
bu süreç gerçekleştirilir ve tüm işlem artık daha karmaşık hale
gelmiştir.
Bağlayıcı normal bir kitaplıkla karşılaştığında, kuşkusuz, eskiden
olduğu gibi davranır.
Paylaşımlı kitaplık nesnel yazılım içeren dosyalardan oluşan
bir belgelik (arşiv) olmaktan çok nesnel yazılımı içeren tek bir
dosya gibidir. Bağlayıcı, paylaşımlı kitaplığı bir programı
bağlarken, kitaplık içinde hangi modülün kitaplığa eklenip
eklenmediğiyle ilgilenmez, yalnızca, çözümlenmemiş kaynakların
çözümlenmesini sağlar ve bunlardan hangilerinin kitaplığın
içerilmesi durumunda dizelgeye (listeye) eklenmesi gerektiğini
saptar. Tüm paylaşımlı kitaplıkları içeren bir belgelik (arşiv)
ar(1) kitaplığı ilke olarak oluşturulabilse de bu yola sık sık
başvurulmaz. Çünkü, paylaşımlı bir kitaplık, genellikle,
çeşitli modüllerin bağlanmasının bir sonucudur ve bu yüzden
kitaplık, daha sonra, çalıştırma süresince gerekecektir. Paylaşımlı
kitaplık deyimi bu yapı için, belki de, en iyi ad değildir ve de
bu yapıyı paylaşımlı nesne olarak adlandırmak daha yerinde olacaktır.
(Ancak, bu diğer adı kullanma eğiliminde değiliz.)
4.- Kitaplık Türleri.
Biraz önce sözünü ettiğimiz gibi Linux altında iki tür kitaplık
vardır; durağan ve paylaşımlı. Durağan kitaplıklar ar(1) yazılımıyla
bir belgelik (arşiv) içine alınmış ve ranlib yazılımıyla da indislenmiş
olan modüller topluluğudur. Bu modüller, çoğunlukla, adının sonunda
uylaşım olarak .a bulunan bir dosya içinde tutulurlar.
(Linux altında dosya uzantısı kavramı söz konusu olmadığından
uzantı terimini kullanmayacağım.) Bağlayıcı addaki .a kesimini
ve sanki durağan bir kitaplık söz konusuymuş gibi, çözümlenmemiş
kaynakları çözümleyen modülleri seçip programa ekleyerek modül
arayışına başlar.
Paylaşımlı kitaplıklar ise, tersine, belgelik (arşiv) olmayıp Üzel
bir düzgü (kod) (kendilerini paylaşımlı kitaplık olarak tanımlayan)
tarafından imlenen (işaretlenebilen) yeniden düzenlenebilir nesnelerdir.
Bağlayıcı ld(1), sözü edildiği gibi, program kaynak yapısına
modülleri eklemez. Ama kitaplık tarafından sağlanan kimliklendiricileri
çözümlemiş gibi onları seçer, kitaplık tarafından tanıtılanları ekler
ve bu durumda izlenimi vermeye çalışan diğer kaynak yazılımları
eklemeksizin işini sürdürür. Bağlayıcı ld(1) paylaşımlı bir kitaplığı
ad sonundaki .so kesiminden algılayabilir (.so.xxx.yyy kesimine bakmaz,
bu noktaya daha sonra değineceğiz).
5.- Linux Altında Bağlanma İşlemleri.
Nesnel modüller içeren her program çalıştırılabilen (executable)
oluşturmak üzere bağlantıya sokulur. Bu işlem, Linux bağlayıcısı
olan, ld(1) tarafından gerçekleştirilir.
ld(1), davranışını yeniden düzenleme olanağı veren türlü
seçenekleri destekler. Ancak, burada yalnızca kitaplık kullanımıyla
genelde ilgili olan seçenekleri gündeme alacağız. ld(1)
doğrudan kullanıcı tarafından etkinleştirilmek yerine derleyicinin
kendisi, gcc(1) tarafından, derleme sürecinin son aşamasında
devreye sokulur. Onun kullanım yolu hakkında yüzeysel bir bilgi
kitaplıkların Linux altında kullanımlarının anlaşılmasında bize
yardımcı olacaktır.
ld(1), işlevini yerine getirebilmek için, programa
bağlanacak olan nesnelerin dizelgesine (listesine) gereksinim duyar.
Bu nesneler, daha önceden sözettiğimiz uylaşıma, yani paylaşımlı
kitaplıkların ad sonlarında .so (.so.xxx.yyy değil) durağan kitaplıkların
ad sonlarında .a (ve kuşkusuz, basit nesne dosyaların ad sonlarında .o)
kesimlerinin bulunması uylaşımına, uymak koşuluyla, herhangi bir sırada(*)
verilebilir ve çağrılabilirler.
(*) Bu bütünüyle doğru değildir. ld(1), yalnızca, kaynakları
kitaplık içerimi anında çözümleyen modülleri kapsama alır. O anda,
daha sonra kapsama alınacak bir modül tarafından gündeme getirilecek
kaynaklar bulunabilir. Bu kitaplığın kapsama alınma anında bu kaynaklar
henüz ortalıkta görünmediklerinden içerilmede kargaşaya neden olabilirler.
Dolayısıyla kitaplıkların kapsama alınma sırası önem kazanabilir.
Öte yandan, ld(1)'nin -l ve -L seçenekleri ölçünlü (standart)
kitaplıkların içerilmesine olanak sağlar.
Amaaa ... Acaba ölçünlü (standart) kitaplıktan ne anlamaktayız,
fark nedir? Hiçbir şey! Tek önemli nokta, ld(1)'in
ölçünlü (standart) kitaplıkları önceden belirlenmiş yerlerde araması,
parametre dizelgelerinde (listelerinde) nesne olarak gözükenleri
ise dosya adlarını kullanarak bulmaya çabalamasıdır.
Kitaplıklar benimsenmiş olarak /lib ve /usr/lib
dizinlerinde (ld(1)'nin yapı ve sürümüne bağlı olarak
bazı ek konumların da kullanıldığını duymakla beraber) aranır.
-L olağan kitaplık aramasında kullanılanlara dizin ekleme
olanağı sağlar. Kullanım, eklenecek her yeni dizin için -L
dizin yazarak gerçekleştirilir. Ölçünlü (standart) kitaplıklar,
Ad yüklenecek kitaplığı belirtmek üzere, -l Ad
seçeneğiyle belirtilirler. ld(1) aramayı ilgili dizinlerde
sırayla yapar. Her bir dizinde, önce libAd.so dosyaadı aranır.
Bulunamazsa, libAd.a ve onun durağan karşılığı denenir.
ld(1)'in libAd.so dosyasını bulması durumunda,
bağlantı paylaşımlı kitaplık gibi bağlamayla gerçekleştirilir.
libAd.a adlı bir dosya bulunursa, çözümlenmemiş kaynakların
herhangi birinin çözümlenmesi durumunda bu dosyadan elde edilecek
modüller bağlanır.
6.- Devinimli Bağlama ve Paylaşımlı Kitaplıkların Yüklenmesi
Devinimli (dinamik) bağlama çalıştırılabilen kaynak yazılımın
yüklenmesi anında /lib/ld-linux.so adlı (gerçekte,
kendisinin de paylaşımlı bir kitaplık olduğu) özel bir modül
tarafından gerçekleştirilir.
Daha doğrusu, devinimli kitaplıkların bağlanması için iki modül
elde bulunmaktadır: eski a.out biçimini kullanan kitaplıklar için
/lib/ld.so ve yeni ELF biçimini kullanan kitaplıklar için
/lib/ld-linux.so.
Bu modüller özel olup her devinimli program bağlamasında
yüklenmelidirler. /lib dizininden başka yere aktarmamak
ve adlarını değiştirmemek amacıyla bunların adları ölçünlüdür
(standarttır). Eğer /etc/ld-linux.so adlı dosyanın
adı değiştirilecek olursa, koşma anında çözümlenmemiş olan
kaynakların çözümlenmesi bu yazılım tarafından
gerçekleştirildiğinden, paylaşımlı kitaplık kullanan programlar
kullanım dışı kalırlar.
/etc/ld.so.cache adlı dosya tarafından desteklenen son
modül her bir kitaplık için böyle bir kitaplığı içermeye en uygun
çalıştırılabilen dosyayı gösterir. Bu konuya daha sonra yine
döneceğiz.
7.- soname. Paylaşımlı Kitaplıkların Sürümleri. Uyumluluk.
Artık paylaşımlı kitaplıklarla ilişkili en yanıltabilici konuya
girebiliriz: Sürümler
Sık sık 'library libX11.so.3 not found' biçiminde
ilgili kitaplığın bulunamadığını vurgulayan ve bizi libX11.so.6
varolmasına rağmen birşey yapamaz durumda bırakan bir iletiyle (mesajla)
karşılaşırız. Nasıl olur da, ld.so(8), libpepe.so.45.0.1
ve libpepe.so.45.22.3 kitaplıklarının yerdeğiştirebileceğini
algılarken libpepe.so.46.22.3?'nin bunlar yerine kullanımına
izin vermez.
Linux altında ve ELF biçimini devreye sokan tüm işletim sistemlerinde
kitaplıklar, birbirlerinden ayırdedilmek amacıyla bir katarla (simge
dizisiyle) kimliklendirilirler: soname.
soname kitaplığın kendisinde içerilir ve ilgili katar (simge dizisi)
kitaplığı oluşturan nesneler bağlanırken belirlenir. Bu katara
bir değer vermek için, paylaşımlı kitaplık yaratıldığında, ld(1)'e bir
seçenek (-soname ) vermek gerekir.
Bu katar çalıştırılabilen dosyayı kimliklendirir ve yüklenmesi
gereken paylaşımlı kitaplığı kimliklendirmek amacıyla devinimli
yükleyici tarafından kullanılır. Süreç aşağıdakine benzer
biçimdedir:
Ld-linux.so programın bir kitaplığa gereksinimi olduğunu saptar
ve kitaplığın soname değerini belirler. Ardından /etc/ld.so.cache
devreye girer ve bu değeri içeren dosyanın adını bulur. Daha sonraki
aşamada, istenen soname ile kitaplıkta bulunan ad karşılaştırılır ve
eğer aynı oldukları saptanırsa iş bitmiş demektir! Özdeşlik sözkonusu
değilse arama süreci uygun değer bulunana dek sürdürülür ya da yakınma
iletisiyle (mesajıyla) işlem durdurulur.
ld-linux.so'un istemde bulunulan soname ile dosyanın
çakıştığını denetlemesinden dolayı, soname bir kitaplığın yükleme için
uygun düşüp düşmediğinin sınanmasına olanak sağlar. Uyumsuzluk durumunda
ünlü 'libXXX.so.Y' not found biçiminde
aranan kitaplığın bulunmadığı doğrultusunda ileti (mesaj) yayınlanır.
Aranan şey soname olup yapılan yakınma soname'e gönderide bulunur.
Bu durum, bir kitaplığın adı değiştirildiğinde karşılaşılan
kavram kargaşasına neden olur ve sorun da ortadan kalkmaz.
Fakat soname'e erişip değiştirmek hiç de iyi bir düşünce değildir.
Çünkü, Linux topluluğunda soname'e atama yapmak için bir uylaşım
bulunmaktadır:
Uylaşım gereği, bir kitaplığın soname'i (paylaşımlı nesnel adı)
uygun bir kitaplığı ve onun ARAYÜZÜNÜ kimliklendirmelidir.
Eğer bir kitaplıkta onun yalnızca iç işlevselliğini etkileyen
tüm arayüzün etkisiz kaldığı (fonksiyonların sayısı, değişkenler,
fonksiyon parametreleri) değişiklikler yapılacak olursa iki kitaplığın
(değişiklik öncesi ve sonrası) yerdeğiştirebilir olduğu söylenir.
Böylece, genellikle, değişikliklerin azınlıkta olduğunu söyleyebiliriz.
(İki kitaplık da uyuşumlu olup birinin yerine diğeri kullanılabilir.)
Bu durumda, soname'de gözükmeyen azınlık değeri sük sük değiştirilebilir
ve büyük sorunlar yaşanmaksızın kitaplıklararası değişim sağlanabilir.
Bununla beraber, fonksiyon eklemelerinde, fonksiyon kaldırımında,
ve genellikle kitaplığın ARAYÜZ DEĞİŞTİRİMİNDE kitaplığın bir öncekisiyle
yerdeğiştirebilir biçimde tutulabilmesi mümkün değildir. (Sözgelimi,
libX11.so.3 yerine libX11.so.6'in yerleştirimi
X11R5'ten X11R6'a güncellemenin bir parçası olup yeni fonksiyon
tanımlarını gündeme getirdiğinden arayüzü değiştirir). X11R6-v3.1.2'dan
X11R6-v3.1.3'ye geçişin arayüzde değişiklikler içermemesi ve (her ne kadar
yenisine, eskisini koruma altında tutabilmek amacıyla, ayrı bir ad verilse
de) kitaplığın aynı soname'e sahip olması olasıdır. Bu nedenle, kitaplık
adında sürüm numarası da verilirken soname'de yalnızca egemen sayı yeralır.
8.- ldconfig(8)
Önceden sözünü ettiğimiz gibi, /etc/ld.so.cache,
tt>ld-linux.so'in kitaplıkta bulunan dosyanın soname'ini
dönüştürmesine izin verir. Bu daha çok etkinlik için ikitabanlı
(binary) birdosya biçiminde olup ldconfig(8) güdümüyle
yaratılır. ldconfig(8), /etc/ld.so.conf
tarafından belirtilen dizinlerde bulunan her bir devinimli kitaplık
için kitaplığın soname'i tarafından çağrılan bir simgesel bağlantı
üretir. Bu eylem ld.so dosya adını elde ederken, gerçekte yapılan
dizin dizelgesinde (listesinde) aranan soname'e sahip dosyanın
seçilmesi olacak biçimde gerçekleştirilir. Böylece, her kitaplık
ekleme sürecinde ldconfig(8)'nin çalıştırılmasına
gereksinim kalmaz. ldconfig, yalnızca dizelgeye (listeye) bir dizin
eklenirken koşulur.
9.- Devinimli Kitaplık Yapmak İstiyorum.
Devinimli (dinamik) bir kitaplık oluşturmadan önce onun gerçekten
yararlı olup olmadığını düşünmek gerekir. Devinimli kitaplıklar sistemde
aşağıdaki nedenlerle aşırı yüklemelere neden olurlar:
Bir programın yüklenmesi çeşitli aşamalarda gerçekleştirilir.
Bunlardan biri anaprogramın yüklenmesi olup diğerleri de
programın kullandığı her bir devinimli kitaplığın devreye
sokulmasıdır. (Bu son aşamanın uygun devinimli kitaplıklar
için, uygun olmama konumundan yarar konumuna geçtiğini
göreceğiz).
Devinimnli kitaplıklar yeniden bellek düzenlemeli güdümler
içermelidir. Bunun nedeni süreç için sanal bulunakların
(adreslerin) bulunduğu yerlerde yükleme bulunağını
(adresinin) yükleme anına kadar bilinmemesidir. Derleyici,
yükleme anında, kitaplığın yüklenme konumunu tutmak
için bir yazmaç alıkoymaya zorlanır. Bu ise, sonuçta, güdümler
topluluğunun eniyilenmesi için yazmaç sayısını bir azaltır.
Bu ise, bu durumda ortaya çıkan aşırı yüklemenin birçok durumda
karşılaşılan aşırı yüklemeden %5 daha fazla olması nedeniyle,
küçük bir saglıksızlıktır.
Devinimli kitaplığın uygun düşebilmesi için zamanın büyük bir
çoğunluğunda bazı programlar tarafından kullanılır olması gerekmektedir.
(Bu, başlatıcı sürecin ölümünden sonra kitaplık metninin yeniden
yüklenmesi sorunundan kaçınmaya olanak sağlar. Diğer süreçler kitaplığın
modüllerini kullanırken kitaplık da bellekte kalır).
Paylaşımlı kitaplık, yalnızca gereken modülleriyle değil, bütünüyle
belleğe yüklenir. Dolayısıyla yararlı olabilmesi için, bütünsel olarak
yararlı olmalıdır. Devinimli kitaplığa en kötü örnek yalnızca bir fonksiyonu
kullanılıp %90'ı çok seyrek kullanılan bir kitaplıktır.
Devinimli kitaplığa bir örnek olarak C standart kitaplığı verilebilir.
(Bu kitaplık C'de yazılan tüm programlar tarafından kullanılır;).
Ortalamada, fonksiyonların tümü orada burada yani her yerde
kullanılırlar.
Durağan kitaplıklarda kullanımları seyrek olan fonksiyonların
içerilmesi genellikle gereksizdir. Bu fonksiyonlar kitaplığın kendi
modülünde yer aldıkça, bunlar kendilerine gereksinimi olmayan
fonksiyonlara bağlantılı olmayacaklardır.
9.1.- Kaynakların Derlenmesi
Kaynakların derlenmesi, sürecin sanal bulunaklar (adresler) uzayındaki
farklı konumlara yüklenebilen güdümler topluluğu üretebilmek amacıyla
'-f PIC' (Konumdan Bağımsız Güdümler Topluluğu) seçeneği kullanımı
dışında, normal bir kaynak durumundakine benzer biçimde
gerçekleştirilir.
Bu aşama, durağan olarak bağlanan bir programda kitaplık nesnelerinin
konumları bağlanma anında yani sabit bir anda çözümlenir olmasından
dolayı, önemlidir. Eski a.out çalıştırılabilenlerinde, bu aşamanın
her bir paylaşımlı kitaplığın sanal bulunaklar (adresler) uzayının
sabit bir konumunda yerleştirilmesiyle sonuçlanarak gerçekleştirilmesi
olanaksızdı. Bunun bir sonucu olarak, bir programın sanal belleğin
örtüşmeli bölgelerinde yüklenmek için hazırlanmış iki kitaplık kullanmak
istemesi durumunda çatışmalar oluşuyordu. Bir dizelgenin (listenin)
bakımı için zorlanmanız durumunda, herhangi bir kimsenin bir kitaplığı
devinimli yapmak istediği bir yerde, başka birilerinin kullanmaması
amacına yönelik olarak bulunak (adres) bölgeleri belirtımı gerekecekti.
Önceden sözünü ettiğimiz gibi, resmi bir dizelgeye (listeye) kayıt
için devinimli kitaplığa gerek yoktur. Bunun nedeni, güdümler
topluluğunun bellekteki konumu yeniden düzenlenebilir olması
gerekmesine rağmen, kitaplığın, yüklenme anında, o anda belirlenen
konumlara gitmesidir.
9.2.- Kitaplıktaki Nesnelerin Bağlanması
Nesnelerin tümünün derlenmesinden sonra onların, devinimli olarak
yüklenebilen bir nesne yaratmak için, özel bir seçenekle bağlanması
gerekir.
gcc -shared -o libName.so.xxx.yyy.zzz
-Wl,-soname,libName.so.xxx
Okuyucunun takdir edeceği gibi, paylaşımlı bir kitaplık yaratımına
yolaçacak bir dizi seçeneğin devreye sokulması dışında, bu işlem olağan
bir bağlama işlemine benzer. Şimdi bunları bir bir açıklayalım:
-shared.
Bu sözcük bağlayıcıya, sonuçta, paylaşımlı bir kitaplık
üreteceğini, dolayısıyla çıktı dosyasında kitaplığa karşılık gelen bir
tür çalıştırılabilenin bulunacağını, söyler.
-o libName.so.xxx.yyy.zzz.
sonuç dosyasının adıdır. Ad uylaşımına uymak zorunlu olmamakla
birlikte, bu kitaplığın gelecekteki gelişmelerin bir ölçünü olması
istenmiyorsa, uylaşıma uymak daha uygundur.
-Wl,-soname,libName.so.xxx.
-Wl seçeneği gcc(1)'ye (virgülle ayrılmış)
izleyen seçeneğin baglayıcı için olduğunu söyler. Bu gcc(1)
tarafından seçeneklerin ld(1)'ye geçirilmesi için kullanılan
düzenektir. Yukarıda, bağlayıcıya aşağıdaki seçenekler
geçirilmektedir.
-soname libName.so.xxx
Bu seçenek kitaplığın soname'ni, kitaplığın yalnızca soname'i
saptanmış kitaplık gerektiren programlar tarafından devreye sokulmasına
olanak verebilecek biçimde saptar.
9.3.- Kitaplık Kurulumu
Artık ilgili çalıştırılabilenin elde olduğu düşünülebilir. Böylece,
bunun, kullanıma olanak verebilecek uygun bir yere yerleştirilmesi
gerekir.
Yeni kitaplığımıza gereksinim duyan bir programı derlemek için
aşağıdaki güdüm satırı kullanılabilir:
gcc -o program libName.so.xxx.yyy.zzz
Kitaplığın uygun bir yere (/usr/lib), kurulmuş olması durumunda
aşağıdaki güdüm satırı yeterlidir:
gcc -o program -lName
(kitaplığın /usr/local/lib'de bulunması durumunda '-L/usr/local/lib'
seçeneğinin eklenmesi yeterlidir)
Kitaplığın kurulumu için aşağıdaki işlemler yapılabilir:
Kitaplığı /lib ya da /usr/lib'a kopyalayın.
Onu ayrı bir konuma (sözgelimi /usr/local/lib), kopyalamaya
karar verirseniz bağlayıcının yani ld(1)'in programları
bağlarken kitaplığı özdevimli olarak bulabileceğinden emin
olamazsınız.
libName.so.xxx.yyy.zzz'den libName.so.xxx.'ye
simgesel bağlantı yapmak için ldconfig(1)'yi çalıştırın. Bu
aşama bize önceki aşamaların doğru biçimde gerçekleştirildiğini ve
kitaplığın devinimli bir kitaplık olduğunu söyler.
Programların bağlanma biçimi, kitaplıkların koşma anında yüklenmesi
dışında, bu aşama tarafından etkilenmez.
Bağlayıcının kitaplığı -l seçeneği ile bulabilmesine izin
vermek için libName.so.xxx.yyy.zzz (ya da
libName.so.xxx, soname)'den libName.so'a,
simgesel bir bağlantı kurunuz. Bu düzeneğin işleyebilmesi için
kitaplığın adının libName.so örüntüsüyle çakışması gerekir.
10.- Durağan bir kitaplık yapımı
Öte yandan durağan bir kitaplık yapımı istendiğinde (ya da durağan
bağlantı kopyaları sunabilmek için iki sürüm gerekecektir) aşağıdaki
biçimde ilerlenebilir:
Not: Bağlayıcı, kitaplık arayışı sırasında, önce libName.so,
daha sonra da libName.a. dosyalarını arar. Eğer her iki
kitaplık da (durağan ve devinimli sürümler) aynı sözcükle adlandırılırsa
bunlardan hangisinin bağlantıya gireceğinin saptanabilmesi genellikle
mümkün değildir (devinimli olanı önce bulunacak olursa hemen bağlantıya
girer).
Bu nedenle, aynı kitaplığın her iki sürümüne de gereksinim
duyulursa, durağan olanının libName_s.a, devinimli olanının da
libName.so olarak adlandırılması salık verilir.
Bu doğrultuda, bağlantı yapılırken durağan sürümü bağlamak için
gcc -o program -lName_s
devinimli olanı bağlamak için de
gcc -o program -lName
güdümleri kullanılacaktır.
10.1.- Kaynakların Derlenmesi.
Kaynakları derlemek için herhangi bir özel ölçüm almayacağız.
Benzer yoldan bağlama aşamalarında nesnelerin konumu kararlaştırıldıkça,
(onu kullanarak devam etmek mümkünse de) -f PIC ile derlemek gerekli
değildir.
10.2.- Kitaplıktaki Nesnelerin Bağlanması
Durağan kitaplıklar durumunda bağlama aşaması yoktur. Tüm nesneler
kitaplık dosyasında ar(1) güdümüyle belgeliklenmişlerdir.
Daha sonra, simgeleri ivedilikle çözümlemek için ranlib(1)
güdümünün kitaplık üzerinde çalıştırılması salık verilir. Gerekli
olmamasına karşın, bu güdümün çalıştırılmaması çalıştırılabilendeki
modüllerin bağlantılarının kopmasına yolaçabilir. Bunun nedeni,
kitaplık oluşturumu süresince modül bağlayıcı tarafından işlenirken
modüller arası dolaylı bağlantıların hepsi de birdenbire
çözümlenmez: modüle bir başka modül tarafından daha sonra
belgelikde (arşivde) gereksinim duyulması durumunda, tüm göndermeler
çözümleninceye dek aynı kitaplık içinden geçirilmek zorunda kalınır).
10.3.- Kitaplık Kurulumu
Yalnızca bir durağan kitaplık kullanılması durumunda durağan
kitaplıkların libName.a biçiminde adlandırılacaktır. İki tür
kitaplık kullanımı durumunda durağan olanlar için, durağan ve
devinimli kitaplık kullanımını kolayca denetleyebilmek amacıyla,
libName_s.a adını salık vereceğim.
Bağlama süreci -static seçeneğinin kullanımına izin verir.
Bu seçenek /lib/ld-linux.so modülünün yüklenmesini denetler
ve kitaplık inceleme sırasını etkilemez. Eğer birileri -static
yazar ve ld(1) devinimli bir kitaplık bulursa onun durgun
eşdeğerini aramak yerine onunla işgörmeyi sürdürür. Bu durum, kitaplıkta
bulunan ve çalıştırılabilene ait olmayan yordamların devreye sokulması,
özdevimli devinimli yükleme modülünün bağlanmaması ve dolayısıyla bu
sürecin gerçekleştirilememesi nedeniyle, koşma anında yanılgılara
yol açar.
11.- Devinimli Bağlantıya Karşı Durağan Bağlantı
Bir program içinde yalnızca durağan olarak içerilmesine izin verilen
bir dağıtım için yetkilendirildiğimiz bir kitaplığı kullanan bir programın
dağıtımını istediğimizi varsayalım. (Bu durum için Motif ile geliştirilen
uygulamalar örnek verilebilir).
Bu tür bir yazılım üretmek için iki yol bulunmaktadır. Birincisi durgun
olarak bağlanmış bir çalıştırılabilen üretmektir. Bu durumda yalnızca
.a kitaplıkları kullanılacak ve devinimli yükleyicinin kullanımından
kaçınılacaktır. Bu tür programlar bir kez yüklenirler ve sistemde,
/lib/ld-linux.so'yi de kapsamak üzere, herhangi bir kitaplık
yüklenimini gerektirmezler. Bununla beraber ikitabanlı (binary)
dosyada gerekli olan yazılımların tümünü taşıma sakıncasına sahip olup
bu yüzden dev dosyalardır. İkinci seçenek devinimli bağlanmış program
üretimidir. Bunun anlamı, uygulamamızın koşacağı çevrenin ilgili tüm
devinimli kitaplıkları sağlaması gerekliliğidir. Bazı durumlarda, sağlanabilen
kitaplıkların tümüne sahip olunamasıyla birlikte çalıştırılabilen
çok küçüktür (sözgelimi Motif'i olmayan bireyler de vardır).
İçindeki kitaplıkların bir kesiminin durağan diğer kesiminin devinimli
olduğu üçüncü bir seçenekten, karışık bir dağıtımdan da süzedilebilir.
Bu durumda, çatışmalı kitaplıkları durağan diğerlerini devinimli seçmek
mantıksal olarak doğru bir yaklaşımdır. Bu seçenek yazılım dağıtımı
için çok uygun bir biçimdir.
Sözgelimi, bir programın üç ayrı sürümü aşağıdaki biçimde
derlenebilir:
gcc -static -o program.static
program.o -lm_s -lXm_s -lXt_s -lX11_s\
-lXmu_s -lXpm_s
gcc -o program.dynamic program.o
-lm -lXm -lXt -lX11 -lXmu -lXpm
gcc -o program.mixed program.o
-lm -lXm_s -lXt -lX11 -lXmu -lXpm
Son durumda, yalnızca Motif kitaplığı Motif (-lXm_s)
durağan olarak bağlanırken diğerleri devinimli olarak bağlanırlar.
Programın koştuğu çevre, libm.so.xx libXt.so.xx libX11.so.xx
libXmu.so.xx y libXpm.so.xx kitaplıklarının uygun sürümlerini
sağlamalıdır.
|