Unix錯誤信息傳遞--errno

這篇博客介紹了unix錯誤處理中重要的概念:errno,介紹了它的定義,作用和注意事項

一. 錯誤總是不可避免的...

在計算機中因為各種內(nèi)外部原因,錯誤是不可避免的,異常處理是程序的組成部分。
比如,我們想獲取一個文件的基本信息,可以調(diào)用系統(tǒng)函數(shù)

int stat(const char *restrict pathname, struct stat *restrict buf);

如果成功,文件的信息會通過第二個參數(shù)buf返回;但也可能會失敗,失敗的原因可能是
文件不存在,也可能是沒有訪問權(quán)限或者其他原因。
那么操作系統(tǒng)如何告訴我們發(fā)生了錯誤?更進一步,操作系統(tǒng)如何告知調(diào)用者錯誤的具體
原因呢?

根據(jù)stat函數(shù)的man文檔,我們知道,可以通過返回值來判斷是否成功,返回值等于0表
示成功,返回-1表示失敗
那具體錯誤原因呢?這時errno就登場了,errno相當(dāng)于一個錯誤碼,當(dāng)stat發(fā)生
錯誤時,會設(shè)置errno的值,我們可以通過檢查errno來得到具體的錯誤信息。

一般的代碼片段如下:

    struct stat sb;
    if (stat("./not_exist.txt", &sb) == -1)
    {
        printf("errorno is: %d\n", errno);
        printf("error msg produced by strerror(): %s\n", strerror(errno));
        perror("error msg produced by perror");
    }

上述代碼中,strerrorperror是兩個有用的函數(shù)。strerror能夠?qū)?code>errno轉(zhuǎn)化為
對應(yīng)錯誤信息的字符串,perror能夠直接打印出錯誤信息,errno沒有出現(xiàn)在perror
的輸入?yún)?shù)中,這個問題后面在講

二 常見錯誤碼

Linux中可以通過man errno.3指令來查看常見錯誤碼,錯誤碼作為常量定義在errno.h
文件中,常見的錯誤碼定義如下(取自errno-bash.h文件):

#define EPERM        1  /* Operation not permitted */
#define ENOENT       2  /* No such file or directory */
#define ESRCH        3  /* No such process */
#define EINTR        4  /* Interrupted system call */
#define EIO      5  /* I/O error */
#define ENXIO        6  /* No such device or address */
#define E2BIG        7  /* Argument list too long */
#define ENOEXEC      8  /* Exec format error */
#define EBADF        9  /* Bad file number */
#define ECHILD      10  /* No child processes */
#define EAGAIN      11  /* Try again */
#define ENOMEM      12  /* Out of memory */
#define EACCES      13  /* Permission denied */
#define EFAULT      14  /* Bad address */
#define ENOTBLK     15  /* Block device required */
#define EBUSY       16  /* Device or resource busy */
#define EEXIST      17  /* File exists */
#define EXDEV       18  /* Cross-device link */
#define ENODEV      19  /* No such device */
#define ENOTDIR     20  /* Not a directory */
#define EISDIR      21  /* Is a directory */
#define EINVAL      22  /* Invalid argument */
#define ENFILE      23  /* File table overflow */
#define EMFILE      24  /* Too many open files */
#define ENOTTY      25  /* Not a typewriter */
#define ETXTBSY     26  /* Text file busy */
#define EFBIG       27  /* File too large */
#define ENOSPC      28  /* No space left on device */
#define ESPIPE      29  /* Illegal seek */
#define EROFS       30  /* Read-only file system */
#define EMLINK      31  /* Too many links */
#define EPIPE       32  /* Broken pipe */
#define EDOM        33  /* Math argument out of domain of func */
#define ERANGE      34  /* Math result not representable */

錯誤碼可以分為兩類:致命錯誤和非致命錯誤。
致命錯誤無法恢復(fù),程序只能立刻終止運行
非致命錯誤大多數(shù)情況下暫時的(比如網(wǎng)絡(luò)短暫異常),程序可以處理這些錯誤,稍后重試,
增強程序健壯性

三 注意事項

errno有兩個性質(zhì):

  1. 正常的系統(tǒng)調(diào)用不會改變errno的值,只有錯誤的系統(tǒng)調(diào)用才會修改errno的值
    這意味著,errno始終記錄著最近一次調(diào)用錯誤的錯誤碼,我們不能通過檢查errno
    判斷是否發(fā)生錯誤,是否發(fā)生錯誤需要通過系統(tǒng)調(diào)用的返回值來判斷,在確定發(fā)生了錯誤
    之后,在去檢查errno獲得具體的錯誤原因。
    這個性質(zhì)也是一開始提到的perror不需要errno作為參數(shù)的原因。
  2. errno不會等于0

四 errno的內(nèi)部實現(xiàn)

errno是一個整數(shù),可以簡單實現(xiàn)為:

extern int errno

上面的實現(xiàn)方式非常簡單,可惜在多線程的情況下會發(fā)生錯誤,因為當(dāng)多個線程需要同時修改
errno時,前一個線程的值會被后一個線程覆蓋,因此,每一個現(xiàn)成需要自己的errno
Linux中,errno的真實定義是:

extern int *_ _errno_location(void);
#define errno (*_ _errno_location())

例子

#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>  //strerror()

/**
 * When an error occurs in one of the UNIX System functions, a negative value 
 * is often returned, and the integer errno is usually set to a value that 
 * tells why
 */
int
main(int argc, char* argv[])
{
    struct stat sb;
    if (stat("./not_exist.txt", &sb) == -1)
    {
        printf("errorno is: %d\n", errno);
        printf("error msg produced by strerror(): %s\n", strerror(errno));
        perror("error msg produced by perror");
    }
    if (stat("./errno.c", &sb) == 0)
    {
        //errno is never cleared by a routine if an error does not occur
        perror("last error msg is");
    } 
}

輸出結(jié)果為:

errorno is: 2
error msg produced by strerror(): No such file or directory
error msg produced by perror: No such file or directory
last error msg is: No such file or directory

參考資料

  • Linux系統(tǒng)文件:/usr/include/asm-generic/errno-base.h
  • Advanced Programming in the Unix Environment(3rd) P14 Error Handling
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容