UNIX/Linux Sanal Dosya Sistemi (VFS)
UNIX/Linux sistemlerinde donanım aygıtları (sabit disk, disket sürücü, fare, seri port, paralel
port ...) birer dosya olarak işlem görür. Bütün bu aygıtlar temel sistem fonksiyonları olan
open, close, read ve write fonksiyonları ile yönetilebilirler. UNIX/Linux sistemlerinde
üzerinde çalışılan aygıt ne olursa olsun aygıta temel sistem fonksiyonları ile hep aynı biçimde
erişilir. Sistemin aygıt bağımsız dosya işlemlerini gerçekleştiren bölümüne sanal dosya
sistemi (Virtual File Sytem) denilmektedir. UNIX/Linux sistemleri 80'li yıllardan itibaren
nesne yönelimli programlama tekniğinden etkilenmiştir. Sanal dosya sistemi çok biçimli
(polimorfik) bir tasarım yapısına sahiptir. Yani programcı open ya da read fonksiyonunu
kullanırken bu işlemler gerçekte ilgili aygıt sürücüsünün open ya da read fonksiyonlarını
çağırır.
Bilindiği gibi bir process yaratıldığında işletim sistemi process'e ilişkin kritik bilgileri bir
process tablosunda saklamaktadır (Win32 sistemlerinde process tablosuna process veritabanı
(process database) denilmektedir). Programcı process üzerinde işlemler yapmak isterse
C ve Sistem Programcıları Derneği 27
process tablosuna erişmekte kullanılan bir handle değerinden faydalanır. Process'in handle
değeri işletim sistemlerinde process'i çalıştıran kişi tarafından doğal olarak elde edilir.
Çalışmakta olan başka process'lerin handle değerleri işletim sisteminin sağladığı çeşitli
fonksiyonlarla elde edilebilmektedir.
Process'e ilişkin process tablosunda pek çok bilgi tutulmaktadır. Bu konudan process yönetimi
kısmında bahsedilecektir. Ancak konu ile ilgili olarak bazı açıklamalar burada yapılacaktır.
Process'in kullanmakta olduğu dosyalar bir biçimde işletim sistemlerinde process tablosunda
tutulmaktadır.
Popüler işletim sistemlerinde dosyalara erişmek için bir handle değeri kullanılır. Win32
sistemlerinde bu değere "File Handle" UNIX/Linux sistemlerinde dosya betimleyicisi (File
Descriptor) denilmektedir. Bir dosya açıldığında işletim sistemi dosya işlemlerini
yönetebilmek için bir alan tahsis eder. Dosyaya erişim için gerekli bilgileri o alana yerleştirir.
Bu alana genellikle dosya nesnesi (file object) denilmektedir. Programcı dosyayı açtığında
handle değeri olarak dosya nesnesinin adresini elde etmez. İşletim sistemi dosya nesnesinin
adresini dosya tablosu denen bir tabloya yazar.
Handle değeri olarak bu tablodaki satır numarasını verir. Dosya tabloları process'e özgüdür ve
girişi process tablosu içerisindedir. Bu tasarım biçimi popüler pek çok işletim sisteminde aynı
biçimde kullanılmaktadır. Örneğin UNIX/Linux sistemleri ile Win32 sistemleri bu bakımdan
benzer tasarıma sahiptir. Örneğin UNIX/Linux sistemlerinde open sistem fonksiyonunu
çağırarak bir dosya açmış olalım ve bize handle değeri olarak (dosya betimleyicisi olarak) 18
değeri verilmiş olsun. Şöyle bir şekil oluşur.
Dosya
Tablosu
Göstericisi
Dosya Tablosu
Dosya Nesnesi
Process Tablosu
(Process Database)
Process ID
Dosya
Betimleyicisi
18
write
Dosya İşlemlerine
İlişkin Adres Dizisi
Dosya
işlemleri
göstericisi
read
C ve Sistem Programcıları Derneği 28
Görüldüğü gibi, bir dosyayı açtığımızda elde edilen dosya betimleyicisi process'e özgü göreli
bir değerdir. Yani biz bu process'te read fonksiyonuna 18 değerini girerek okuma yaparsak,
bu dosyadan okuma yaparız, ama başka bir process bu değerle başka dosyadan okuma
yapabilir.
Dosya tablosunun uzunluğu genellikle dinamik olarak ayarlanmaz, bu uzunluk baştan
belirlenmiştir. Tablonun uzunluğu aynı anda açık olabilecek dosya sayısını belirtmektedir.
Örneğin Linux2.X kernel versiyonlarında bu sayı 256 biçimindedir.
Modern UNIX/Linux sistemlerinde VFS dosya sistemi içerisinde tüm aygıtlar üzerinde
işlemler read, write gibi klasik POSIX fonksiyonları ile yapılır. Bu fonksiyonlar nesne
yönelimli bir yaklaşımla aygıta ilişkin özel read, write gibi fonksiyonları çağırırlar. Bu durum
tipik olarak dosya nesnesi içerisinde dosya işlemlerini yapacak olan fonksiyonların adreslerini
tutan bir tablo yoluyla yapılır. Görüldüğü gibi sistem fonksiyonlarını bir dosya
betimleyicisiyle çağırdığımızda gerçekte hangi fonksiyonların çalıştırılacağı bu
betimleyicilerin gösterdiği dosya nesnelerine bağlıdır. Aslında bu işlemin C++’taki çok
biçimli karşılığı şöyledir:
class FileObject {
public:
virtual int read(...) = 0;
virtual int write(...) = 0;
private:
//...
//...
};
sysread(FileObject *pFile, ...)
{
pFile->read(...);
}
UNIX/Linux sistemleri pek çok popüler dosya sistemini desteklemektedir. Bu dosya
sistemlerinin disk organizasyonları birbirinden farklıdır. Her ne kadar farklı olsa da
UNIX/Linux sistemleri bunları bellekte bir super block yapısı içerisinde ifade ederler. super
block yapısı her dosya sistemi bilgilerini tutabilecek şekilde organize edilmiştir. Super block
yapıları kendi aralarında bir bağlı liste biçiminde tutulur. Aygıta ilişkin super block yapısı
mount işlemi sırasında oluşturulmakta, umount işlemi ile de yok edilmektedir. İlgili dosya
sistemine erişimde veri yapısı olarak giriş noktası super block yapısıdır.
Neden dosya handle değeri olarak dosya tablosunda bir offset verilmektedir de doğrudan
dosya nesnesinin adresi verilmemektedir? Bu tasarımda birincil amaç yönlendirme
(redirection) yapılabilmektedir. Örneğin dosya tablosu ile oynanılarak dosya betimleyicilerine
ilişkin nesne adresleri değiştirilebilir. Böylelikle programda hiç bir değişiklik yapmadan
programın çalıştığı dosyalar değiştirilebilmektedir. İkincil olarak dosya nesnesine doğrudan
erişilmemesi daha güvenli bir yöntemdir. Bu dosya sisteminin tasarımı Win32 sistemlerinde
de benzer biçimdedir.