POSIX Fonksiyonlarında Başarısızlık Nedeninin Tespit

Transkript

POSIX Fonksiyonlarında Başarısızlık Nedeninin Tespit
POSIX Fonksiyonlarında Başarısızlık Nedeninin Tespit Edilmesi
Kaan Aslan
12 Mayıs 2006
POSIX sistem fonksiyonlarının çok büyük çoğunluğu başarı durumunda 0 değerine
başarısızlık durumunda ise –1 değerine geri dönmektedir. Fonksiyonlar başarısızlık
durumunda başarısızlığın nedenini anlatan bir sayısal değeri errno isimli global bir
değişkene yerleştirirler. Yani biz başarısızlığı tespit ettikten sonra bunun nedenini
errno değişkeni içerisindeki değere bakarak tespit edebiliriz. errno değişkeninin
extern bildirimi <errno.h> başlık dosyası içerisindedir. Bu başlık dosyasında ayrıca
errno değişkeninin alabileceği hata değerleri E öneki ile başlayan sembolik sabitler
biçiminde define edilmiştir. Hatanın nedenini anlamak için bu sembolik sabitleri
kullanabilirsiniz. Aşağıda Linux sistemlerindeki <errno.h> dosyasının bu sembolik
sabitlere ilişkin bir kısmını görüyorsunuz:
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
EPERM
ENOENT
ESRCH
EINTR
EIO
ENXIO
E2BIG
ENOEXEC
EBADF
ECHILD
EAGAIN
ENOMEM
EACCES
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Operation not permitted */
No such file or directory */
No such process */
Interrupted system call */
I/O error */
No such device or address */
Arg list too long */
Exec format error */
Bad file number */
No child processes */
Try again */
Out of memory */
Permission denied */
Burada önemli bir noktayı belirtmek istiyoruz. POSIX standartlarında errno global
değişkeninin içerisindeki değerlerin sayısal karşılıklarının ne olacağı hakkında bir
belirlemede bulunulmamıştır. Yalnızca hata nedenlerini anlatan sembolik sabitlerin
neler olacağı ve bu sembolik sabitlerin hangi hataları belirttiği açıklanmıştır. Bu
durumda örneğin her POSIX sisteminde <errno.h> başlık dosyası içerisinde
EACCESS isimli sembolik sabit tanımlıdır, fakat bu sembolik sabit her sistemde
başka başka değerlere define edilmiş olabilir. O halde kodumuzun taşınabilirliğini
bozmamak için o sembolik sabitleri tercih etmeliyiz. Örneğin:
#include <errno.h>
...
herhangi_bir_posix_fonksiyonu();
if (errno == EACCESS) {
....
}
Linux sistemlerinde EACCESS değeri 13 olduğu halde başka sistemlerde farklı
olabilir.
1
Kaan Aslan Makale Arşivi – www.kaanaslan.net
errno standartlara göre int türden bir nesne belirtmek zorundadır. Bu durumda errno
int türden bir değişken olabileceği gibi, bir makro da olabilir. Fakat bizim errno isimli
ifadeye bir atama yapabiliyor olmamız gerekir. Örneğin POSIX standartlarına göre
her thread’in ayrı bir errno değeri olmak zorundadır. Bunun için errno değişkeninin
thread’e özgü alanda yaratılması ve thread specific data olarak o alandan çekilmesi
gerekir. Bu da ancak fonksiyon çağrısı ile yapılabilir. Örneğin:
#define errno (*(__errno_location()))
errno değişkenine ancak POSIX fonksiyonu başarısız olduğunda bakılmalıdır. Son
çağrılan fonksiyonun başarılı olması durumunda errno değişkeni 0 gibi özel bir
değere çekilmek zorunda değildir.[1]
POSIX standartlarında her fonksiyon için o fonksiyonun başarısızlık durumunda errno
değişkenini hangi hata değeriyle dolduracağı tek tek açıklanmıştır. Hiçbir fonksiyon
errno değişkenine POSIX standartlarında belirtilen hata değerlerinden başka bir
değer yerleştirmez. Yani fonksiyonların hangi nedenlerle başarısız olabileceği
önceden bilinebilmektedir.[2]
Bir POSIX fonksiyonunda oluşan hatanın nedenini yazdıracak olalım. Bu bir switch
içerisinde yapılabilir:
char *msg;
if ((fd = open("test.dat", O_RDONLY)) < 0) {
switch (errno) {
case EACCES:
msg = "Permission denied";
break,
case ENOENT:
msg = "No such file or directory";
break;
case EINVAL:
msg = "Invalid argument";
break;
...
}
fprintf(stderr, "%s\n", pMsg);
}
Böyle bir işlemin oldukça zahmetli olduğunu söylemeye gerek var mı, bilemiyoruz.
Hata mesajlarının yazdırılmasını kolaylaştırmak için birkaç standard POSIX
fonksiyonundan faydalanılabilir. perror isimli fonksiyon errno global değişkeninin
içerisindeki değere bakarak hata değerine ilişkin bir yazıyı stderr dosyasına (yani
varsayılan durumda ekrana) yazdırmaktadır. Prototipi aşağıdaki gibidir:
void perror(const char *s);
perror aslında prototipi <stdio.h> içerisinde olan standart bir C fonksiyonudur. perror
fonksiyonu önce parametresiyle belirtilen yazıyı, sonra bir : karakterini sonra bir tane
boşluk karakterini ve sonra da errno değişkeninin içerisindeki değere ilişkin yazıyı
yazdırır. Örneğin:
2
Kaan Aslan Makale Arşivi – www.kaanaslan.net
if ((fd = open(("test.dat", O_RDONLY)) < 0) {
perror("open");
exit(EXIT_FAILURE);
}
open fonksiyonunun -1 ile geri döndüğünü ve errno değişkenine ENOENT değerinin
yerleştirildiğini varsayalım. Bu durumda stderr dosyasına şunlar yazdırılacaktır:
open: No such file or directory
perror fonksiyonu mesajları İngilizce yazdırmaktadır. Mesajların başka bir dilde
(örneğin Türkçe) yazdırılmasının standart ve kolay bir yolu yoktur.
Komut satırında çalıştırdığınız komutların da hata mesajlarını perror fonksiyonuyla
yazdırdığına tanık olmuşsunuzdur. Örneğin:
errno değerine karşılık gelen hata yazısını doğrudan değil de işleme sokarak
yazdırmak isteyebilirsiniz. Bunun için öncelikle bu yazıyı elde etmeniz gerekebilir.
strerror fonksiyonu bu işi yapmaktadır:
char *strerror(int errnum);
fonksiyon hata kodunu parametre olarak alır ve hata yazısının yerleştirildiği static
alanın adresi ile geri döner. Bu fonksiyonun thread güvenli biçimi de vardır:
int strerror_r(int errnum, char *strerrbuf, size_t buflen);
Bazı POSIX fonksiyonlarında başarısızlık durumu geri dönüş değeri yoluyla
belirlenememektedir. Örneğin getpwent fonksiyonu hem parola dosyasının sonuna
gelindiğinde hem de okuma hatası olduğunda (tabi çok seyrek ortaya çıkabilir) NULL
adresle geri döner. Parola dosyasınının sonuna gelinmiş olması normal bir durumdur.
İşte bu tür durumlarda fonksiyonu çağırmadan önce errno değişkenine 0 yerleştirip
çıkışta errno değerine bakıp başarısızlığı belirleyebiliriz:
struct passwd ent;
...
errno = 0;
while ((ent = getpwent()) {
...
}
if (errno != 0) {
perror("getpwent");
exit(EXIT_FAILURE);
}
Bazı POSIX fonksiyonları ise errno değişkenine değer atamaz. Başarısızlık nedenine
ilişkin hata koduyla geri dönerler. Örneğin thread fonksiyonları böyledir:
3
Kaan Aslan Makale Arşivi – www.kaanaslan.net
result = pthread_create(...);
if (result) {
fprintf(stderr, "%s\n", strerror(result));
exit(EXIT_FAILURE);
}
Bir POSIX fonksiyonu çalıştırıldığında birden fazla hata ile karşılaşılıyor olabilir. Bu
durumda fonksiyonun hangi hata değeri ile geri döneceği ya da errno değişkenine
yerleştireceği konusunda bir belirlemede bulunulmamıştır. Bu değerlerden herhangi
biri olabilir.
[1]
Yukarıda da belirtildiği gibi sistem fonksiyonlarının çoğu başarısızlık durumunda -1
özel değerine geri dönmektedir. Fakat bazı programcılar sistem fonksiyonunun
başarısızlığını –1 değeriyle karşılaştırma yaparak değil, < 0 biçiminde sorgularlare.
Örneğin:
if (stat(...) < 0) {
...
}
Mikro düzeyde bakıldığında –1 le karşılaştırma yerine < 0 karşılaştırması daha etkin
bir makine kodu üretilmesine yol açacağı söylenebilir. Tabii böylesi olası bir kazancın
programınız için ciddi bir anlamı yoktur.
[2]
Windows sistemlerinde son çağrılan API fonksiyonunda oluşan hata değeri
GetLastError fonksiyonuyla elde edilebilir. Fakat bu sistemlerde API fonksiyonlarının
hangi nedenlerle başarısız olacağı Microsoft tarafından tam olarak belgelenmemiştir.
API fonksiyonlarının birbirlerini çağırdığı karmaşık içerisinde böyle bir belirlemenin
zor olması bunun önemli bir nedenidir.
4
Kaan Aslan Makale Arşivi – www.kaanaslan.net

Benzer belgeler