這篇博客介紹了unix錯(cuò)誤處理中重要的概念:
errno
,介紹了它的定義十电,作用和注意事項(xiàng)
一. 錯(cuò)誤總是不可避免的...
在計(jì)算機(jī)中因?yàn)楦鞣N內(nèi)外部原因,錯(cuò)誤是不可避免的疚沐,異常處理是程序的組成部分。
比如潮模,我們想獲取一個(gè)文件的基本信息亮蛔,可以調(diào)用系統(tǒng)函數(shù)
int stat(const char *restrict pathname, struct stat *restrict buf);
如果成功,文件的信息會(huì)通過(guò)第二個(gè)參數(shù)buf
返回再登;但也可能會(huì)失敗尔邓,失敗的原因可能是
文件不存在晾剖,也可能是沒(méi)有訪問(wèn)權(quán)限或者其他原因锉矢。
那么操作系統(tǒng)如何告訴我們發(fā)生了錯(cuò)誤?更進(jìn)一步齿尽,操作系統(tǒng)如何告知調(diào)用者錯(cuò)誤的具體
原因呢沽损?
根據(jù)stat
函數(shù)的man文檔,我們知道循头,可以通過(guò)返回值來(lái)判斷是否成功绵估,返回值等于0表
示成功,返回-1表示失敗
那具體錯(cuò)誤原因呢卡骂?這時(shí)errno就登場(chǎng)了国裳,errno相當(dāng)于一個(gè)錯(cuò)誤碼,當(dāng)stat
發(fā)生
錯(cuò)誤時(shí)全跨,會(huì)設(shè)置errno的值缝左,我們可以通過(guò)檢查errno來(lái)得到具體的錯(cuò)誤信息。
一般的代碼片段如下:
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");
}
上述代碼中浓若,strerror
和perror
是兩個(gè)有用的函數(shù)渺杉。strerror
能夠?qū)?code>errno轉(zhuǎn)化為
對(duì)應(yīng)錯(cuò)誤信息的字符串,perror
能夠直接打印出錯(cuò)誤信息挪钓,errno
沒(méi)有出現(xiàn)在perror
的輸入?yún)?shù)中是越,這個(gè)問(wèn)題后面在講
二 常見(jiàn)錯(cuò)誤碼
Linux中可以通過(guò)man errno.3
指令來(lái)查看常見(jiàn)錯(cuò)誤碼,錯(cuò)誤碼作為常量定義在errno.h
文件中碌上,常見(jiàn)的錯(cuò)誤碼定義如下(取自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 */
錯(cuò)誤碼可以分為兩類:致命錯(cuò)誤和非致命錯(cuò)誤倚评。
致命錯(cuò)誤無(wú)法恢復(fù),程序只能立刻終止運(yùn)行
非致命錯(cuò)誤大多數(shù)情況下暫時(shí)的(比如網(wǎng)絡(luò)短暫異常)馏予,程序可以處理這些錯(cuò)誤天梧,稍后重試,
增強(qiáng)程序健壯性
三 注意事項(xiàng)
errno
有兩個(gè)性質(zhì):
- 正常的系統(tǒng)調(diào)用不會(huì)改變
errno
的值吗蚌,只有錯(cuò)誤的系統(tǒng)調(diào)用才會(huì)修改errno
的值
這意味著腿倚,errno
始終記錄著最近一次調(diào)用錯(cuò)誤的錯(cuò)誤碼,我們不能通過(guò)檢查errno
來(lái)
判斷是否發(fā)生錯(cuò)誤,是否發(fā)生錯(cuò)誤需要通過(guò)系統(tǒng)調(diào)用的返回值來(lái)判斷敷燎,在確定發(fā)生了錯(cuò)誤
之后暂筝,在去檢查errno
獲得具體的錯(cuò)誤原因。
這個(gè)性質(zhì)也是一開(kāi)始提到的perror
不需要errno
作為參數(shù)的原因硬贯。 -
errno
不會(huì)等于0
四 errno的內(nèi)部實(shí)現(xiàn)
errno
是一個(gè)整數(shù)焕襟,可以簡(jiǎn)單實(shí)現(xiàn)為:
extern int errno
上面的實(shí)現(xiàn)方式非常簡(jiǎn)單,可惜在多線程的情況下會(huì)發(fā)生錯(cuò)誤饭豹,因?yàn)楫?dāng)多個(gè)線程需要同時(shí)修改
errno
時(shí)鸵赖,前一個(gè)線程的值會(huì)被后一個(gè)線程覆蓋,因此拄衰,每一個(gè)現(xiàn)成需要自己的errno
Linux中它褪,errno的真實(shí)定義是:
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