2012-07-10 8 views
9

Nel solito caso open() restituisce il nuovo descrittore di file o -1 se si è verificato un errore e in tal caso, errno è impostato in modo appropriato.perché fopen() o open() usano errno invece di restituire il codice di errore?

Non capisco perché questo meccanismo di errno sia utilizzato qui? qual è lo scopo di qui? perché proprio non possiamo mappare tutti gli errori con un ritorno negativo no?

come

fd = open("/dev/tty0", O_RDWR | O_SYNC); 
if(fd == -1) 
    printf("this is EACCES error"); 
else if (fd == -2) 
    printf("this is EPERM error"); 

C'è qualche benifit di errno meccanismo.? se sì allora mi piacerebbe sapere/capire poi in altre cose posso anche usare questo meccanismo.

risposta

11

Dal fopen restituisce un tu FILE* non posso aspettarmi che restituisca un codice di errore in quel puntatore: l'unico valore "speciale" per i puntatori è 0.

Come si osserva, per open questa restrizione non viene mantenuta. In effetti i sistemi come Linux fanno esattamente quello che proponi nei loro livelli inferiori. La chiamata di sistema sotto il cofano restituisce il codice di errore negativo se le cose vanno male. Il codice (negato) viene quindi inserito in errno da un wrapper utente superficiale che restituisce lo -1 per indicare l'errore nell'applicazione.

Il motivo per cui questo è fatto è puramente storico.Nei bei vecchi tempi non c'era threading e errno era ancora solo una semplice variabile globale. A quel tempo la strategia scelta non ha subito molti sovraccarichi e probabilmente sembrava un modo accettabile per comunicare tra sistema operativo e applicazione. Dal momento che tali interfacce non possono essere sostanzialmente cambiate senza rompere un sacco di codice, saremo bloccati con errno come una pseudo variabile che è thread locale.

Questo non è l'ideale, ma l'overhead non è così male come sembra, dal momento che questi sono chiaramente errore indicazioni che si dovessero verificare solo in via eccezionale.

+0

+1 molto bella risposta –

1

errno è un codice di errore. È importante mappare gli errori a ciò che sta effettivamente accadendo in modo da poter prendere decisioni strategiche nel codice su cosa fare dopo. Ad esempio, ERANGE definito in errno.h, ti dirà che il risultato di strtol("0xfffffffff",NULL,0) è fuori dall'intervallo di quella funzione. Ancora più importante nel tuo esempio, è bene sapere se hai un errore EACCES o EPERM in modo da sapere come gestire il file.

Non è possibile mappare tutti i problemi con un codice di errore poiché si potrebbero avere più problemi che si desidera catturare e gestire. Quando dico catch, non intendo try/catch.

L'uso di errno stabilisce e meccanismo di gestione degli errori in modo da ottenere più informazioni di solo -1.

ERANGE, EACCES, EPERM e altri sono considerati macro mappati a un particolare numero di errore per scopi di comodità.

9

Per me il vantaggio è che ottenere le informazioni di errore è unificata in quel modo, tornare un valore negativo avrebbe funzionato bene con open in quanto restituisce un intero, ma fopen restituisce una FILE * così un'altra tecnica avrebbe dovuto essere utilizzato lì.

+0

FILE * è ancora puntatore a qualche buffer/memoria, quindi non sarà mai negativo, quindi se si verifica un errore, allora non restituiamo FILE * con valore negativo? perché NULL è 0 –

+6

Un puntatore ha - almeno sulle architetture che conosco - nessun concetto di signness, può (teoricamente) coprire l'intero intervallo del "intero non firmato sottostante" (cioè un punto a 32 bit può andare da 0x00000000 a 0xffffffff) . E 'NULL' è giusto e l'indicazione per dire" hey, questo non è riuscito, guarda a errno per vedere cosa è andato storto " – fvu

+0

In teoria potrebbe essere usato anche un insieme di puntatori riservati; tutto ciò che serve è 'char err_ptrs [MAX_ERRNO]; void * EACCESS_ptr = err_ptrs + EACCESS; 'ecc. per rendere' EACCESS_ptr' un "codice di errore" valido per i puntatori. Tuttavia, dovendo duplicarli come numeri interi e puntatori, e il fatto che alcune interfacce utilizzino l'intero spazio dei valori interi come ritorni validi, rende ancora inutile la pena. –

1

Assegnare a ciascuna funzione un insieme distinto di valori di ritorno rende eccessivamente complicato scrivere codice in modo generico. Con la semantica attuali, si può adottare un modello universale:

int fd; 
if ((fd = some_function(arg1, arg2)) == -1) 
{ 
    perror("some_function"); 
    exit(1); 
} 

Si può anche avvolgere questo in una macro:

#define CALL_OR_DIE(function, ret, ...)  \ 
    if ((ret = function(__VA_ARGS__)) == -1) \ 
    { perror(#function); exit(1); } 

Usage:

int fd; 
CALL_OR_DIE(open, fd, "/dev/tty0", O_RDWR | O_SYNC); 
+1

Quindi stai fondamentalmente dicendo (1) è meglio scrivere '== -1' di' <0' (con errnos definito come negativo), e (2) questa è la motivazione per 'errno'? Suppongo che ci sia un vantaggio nel non dover passare 'fd' a' perror' nel caso di errore, perché può essere letto dall'errore 'locale-thread', ma non penso che spieghi del tutto perché l'errore non sia 't tornato da 'open' pure. –

Problemi correlati