C’nin Standart Dosya Fonksiyonları ve Bu Fonksiyonların POSIX Fonksiyonlarıyla
İlişkisi
C’nin standart dosya fonksiyonları hangi sistemde yazılmışlarsa o sistemin gerçek sistem
fonksiyonlarını çağırırlar. Örneğin fread fonksiyonu UNIX/Linux sistemlerinde read
fonksiyonunu, Win32 sistemlerinde ise ReadFile fonksiyonunu çağırmaktadır. stdio.h
içerisinde prototipi olan printf, scanf gibi fonksiyonlar da aslında birer dosya fonksiyonlarıdır.
Örneğin printf default olarak stdout dosyasını kullanmaktadır. C’nin standart doysa
fonksiyonlarının en önemli özelliği tamponlu (buffered) bir çalışma sunmasıdır.
Aslında modern sistemlerin hemen hepsinde dosya işlemlerine ilişkin sistem fonksiyonları da
bir tampon mekanizması ya da bir cache sistemi kullanmaktadır. Ancak terminolojide
tamponlamalı dosya fonksiyonları denildiğinde çekirdek tarafından yapılan tamponlama değil
kullanıcı alanı içerisinde yapılan tamponlama anlaşılmaktadır. Sistem fonksiyonlarının
kullandığı tampon bölgeler çekirdeğin bellek alanı içerisindedir. Halbuki standart C
fonksiyonlarının kullandığı tampon bölgeler kullanıcı alanı içerisindedir. Aslında modern
sistemlerde standart C fonksiyonları iki kez tamponlamaya yol açmaktadır. Böylece hız
kazanmak bir yana, yavaşlamaya yol açmaktadır. Fakat C’nin standart dosya fonksiyonları
başından beri bir tampon mekanizması içerisinde tasarlanmıştır. Standartlarda anlatımlar ve
fonksiyonlar tamamen tamponlu bir çalışma üzerine kurulmuştur. Özetle C’nin dosya
fonksiyonlarının tamponlu olması standartlara yansımış temel özelliklerdendir. C’nin dosya
fonksiyonları hemen işletim sisteminin sistem fonksiyonlarını çağırmak zorunda değildir.
Bilgileri bir tampon alanda tutup istediği bir zamanda sistem fonksiyonunu çağırır. Dosyanın
tazelenmesi (flush edilmesi) yani fflush fonksiyonunun çağırılması tampondaki bilginin
sistem fonksiyonları çağırılarak dosyaya aktarılması anlamına gelir. fclose işlemi ile ya da
dosya kapatılmamışsa process’in sonlanması ile tazeleme işlemi yapılmaktadır. Tazeleme
işleminin dosyanın yalnızca okuma modunda açıldığında hiç bir anlamı yoktur. Çünkü
tazeleme tampondan dosyaya doğru yapılan bir işlemdir. stdin dosyası da C’de yalnızca
okunabilen bir dosya olarak kabul edilir. Bu nedenle fflush(stdin); işleminin de bir
C ve Sistem Programcıları Derneği 36
anlamı yoktur, ancak pek çok derleyicide bu işlem stdin dosyasının kullandığı tamponu
boşaltma anlamına gelir.
stdout dosyasına bir şey yazdığımız zaman hemen ekranda görünür mü? Standartlara göre
stdout dosyasına yazılanların tazelenmesi için iki şey gerekir.
1) Yazının sonuna ‘\n’ koymak
2) fflush(stdout); yapmak
lseek Fonksiyonu
lseek fonksiyonu dosya göstericisini konumlandırmakta kullanılır. Tipik olarak fseek standart
C fonksiyonu bu fonksiyonu kullanmaktadır (Bu fonksiyonun eski sistemlerdeki ismi seek
fonksiyonu idi, sonra parametrenin offset değerinin long olması dolayısıyla lseek biçimine
dönüştürülmüştür). Prototipi unistd.h başlık dosyası içerisindedir.
off_t lseek(int fildes, off_t offset, int whence);
Fonksiyonun birinci parametresi dosya betimleyicisi, ikinci parametresi konumlandırılacak
offset değeri, son parametresi ise konumlandırma referansıdır. Son parametre şu biçimde
olabilir:
#define SEEK_SET (0)
#define SEEK_CUR (1)
#define SEEK_END (2)
off_t türü tipik olarak sistemlerde long biçimindedir. Fonksiyon başarılıysa yeni
konumlandırılan offset değerine, başarısızsa –1 değerine geri döner (POSIX fonksiyonlarının
çok büyük çoğunluğu başarısızsa –1 değerine geri dönmektedir. Bu fonksiyonlar başarılıysa
herhangi bir negatif değere geri dönmemektedir. Bu nedenle bazı programcılar daha etkin
olduğu gerekçesiyle hata karşılaştırmasını ‘== -1’ yerine ‘< 0’ biçiminde yaparlar). Ancak
lseek fonksiyonu bazı aygıtlar için negatif konumlandırma da yapabilmektedir. Bu nedenle
lseek fonksiyonunun hata testi eğer yapılacaksa ‘== -1’ şeklinde yapılmalıdır.
Dosyalardaki Delikler
lseek fonksiyonu ile bir dosyanın uzunluğunun ötesine konumlandırma yapılabilir. Örneğin
dosyanın uzunluğu 1000 olduğu halde biz aşağıdaki gibi lseek fonksiyonu ile 10000’inci
offset’e konumlandırma yapabiliriz.
lseek(fd, 10000, SEEK_SET);
Bu işlem sonrasında dosyanın uzunluğu konumlandırılan offset’e çekilir yani arttırılır. Ancak
artan bölüm gerçek anlamda disk üzerinde blok temelinde tahsis edilmez. Bu biçimde
büyütülen dosya alanına delik (hole) denir. Delikten okuma yapıldığında sistem fonksiyonları
sıfır okur. Delik kısmına yazma yapılabilir. Şüphesiz sistem deliklerin dosya içerisindeki
yerlerini tutarak yazma sırasında gerçek blok tahsisatını yapmaktadır. Örneğin gerçekte 1000
uzunluğunda olan dosya delik oluşturularak 10000 byte’a çıkarılmış olsun, ls komutu
(dolayısıyla stat fonksiyonu) uzunluk olarak delikli uzunluğu rapor edecektir. Ancak du
C ve Sistem Programcıları Derneği 37
komutu uygulandığında dosya için tahsis edilen blok sayısının artmadığı görülecektir (du
komutunda bildirilen blok miktarının cluster anlamındaki blok ile bir ilgisi yoktur, Linux
sistemlerinin çoğu default olarak cluster anlamındaki bloğu 8 sektör = 4096 olarak alırlar).
Delik oluşturabilmek için dosya uzunluğunun ötesine konumlandırdıktan sonra bazı
sistemlerde write işlemi yapmak gerekir. Delik oluşturma POSIX standartlarında belirtilmiş
standart bir davranış değildir.
/* lseek.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int fd;
if ((fd = open("a.dat", O_RDWR)) == -1) {
perror("open error\n");
exit(EXIT_FAILURE);
}
if (lseek(fd, 10000000, SEEK_SET) == -1) {
perror("lseek error\n");
exit(EXIT_FAILURE);
}
if (write(fd, "kaan", 4) == -1) {
perror("write error");
exit(1);
}
close(fd);
return 0;
}