处理来自系统调用和库函数调用的错误

处理来自系统调用和库函数调用的错误

​ 几乎每个系统调用和库函数都会返回某类状态值,用以表明调用成功与否。要了解调用是否成功,必须坚持对状态值进行检查。若调用失败,那么必须采取相应行动。至少,程序应该显示错误消息,警示有意想不到的事件发生。

​ 不检查状态值,少敲儿个字,听起来的确诱人(尤其是见识到了不检查状态值的UNIX/Linux 程序以后),但实际却得不偿失。认定系统调用或库函数“不可能失败“,不对状态返回值进行检查,这会浪费掉大把的程序调试时间。

​ 少数几个系统函数在调用时从不失败。例如, getpid()总能成功返回进程的ID, 而_exit()
总能终止进程。无需对此类系统调用的返回值进行检查。

处理系统调用错误

​ 每个系统调用的手册页记录有调用可能的返回值,并指出了哪些值表示错误。通常,返回值为-1表示出错。因此,可使用下列代码对系统调用进行检查:

fd = open(pathname, flags, mode);
if(fd == -1){
    //code to handle the error.
}

if(clode(fd) == -1){
    //code to handle the error.    
}

​ 系统调用失败时,会将全局整形变量errno 设置为一个正值,以标识具体的错误。程序应包含<errno.h>头文件,该文件提供了对errno的声明,以及一组针对各种错误编号而定义的常量。所有这些符号名都以字母E打头在每个手册页内标题为ERRORS的章节内,都刊载有一份相应系统调用可能返回的errno值列表。以下便是利用errno诊断系统调用错误的一个简单示例:

cnt = read(fd, buf, numbytes);
if(cnt == -1){
    if(errno == EINTR)
    	fprintf(stderr, "read was interruped by a signal\n");
    	else{
            //some other error occured.
    	}
}

​ 如果调用系统调用和库函数成功, errno绝不会被重置为0, 故此,该变量值不为0, 可能是之前调用失败造成的。此外,SUSv3 允许在函数调用成功时,将errno设置为非零值(当然,几乎没有函数会这么做)。因此,在进行错误检查时,必须坚持首先检查函数的返回值是否表明调用出错,然后再检查errno确定错误原因。

​ 少数系统调用(比如,getpriority()) 在调用成功后,也会返回-1 。要判断此类系统调用是否发生错误应在调用前将errno 置为0 , 并在调用后对其进行检查(上述手法同样适用于某些库函数)。

​ 系统调用失败后,常见的做法之一是根据errno 值打印错误消息。提供库函数perror()strerror(), 就是出于这一目的。

fd = open(pathname, flags, mode);
if(fd == -1){
    perror("open");
    exit(EXIT_FAILURE);
}

​ 函数strerror()会针对其errnum参数中所给定的错误号,返回相应的错误字符串。

#include <string.h>
char* strerror(int errnum);
处理来自库函数的错误

​ 不同的库函数在调用发生错误时,返回的数据类型和值也各不相同。(参见每个函数的手册页。) 从错误处理的角度来说,可将库函数划分为以下几类

  • 某些库函数返回错误信息的方式与系统调用完全相同返回值为-1, 伴之以errno号来表示具体错误remove()便是其中一例,可使用该函数来删除文件(调用unlink()系统调用)或目录(调用rmdir()系统调用) 。对此类函数所发生的错误进行诊断,其方式与系统调用完全相同。

  • 某些库函数在出错时会返回- 1之外的其他值,但仍会设置errno来表明具体的出错情况。例如, fopen()在出错时会返回一个NULL 指针,还会根据出错的具体底层系统调用来设置errno 。函数perror()strerror()都可用来诊断此类错误。

  • 还有些函数根本不使用errno 。对此类函数来说,确定错误存在与否及其起因的方法各不相同,可见诸千相应函数的手册页中,不应使用errnoperror()strerror()来诊断错误。

上一篇:errno错误对照表


下一篇:socketserver OSError:[Errno 98] Address already in use