Unix系统的错误处理

所有的系统调用,都有可能产生错误。通常它们通过返回值-1说明发生了错误。有些时候需要知道发生了哪种错误,出于这个目的,所有的系统调用在适合的时候,都会在一个称为errno的外部整数中留下一个错误号。例如,通过使用errno,程序可以判定打开文件失败是由于它不存在,还是由于用户没有读权限。另外还有一个字符串数组sys_errlist,它以errno为下标,可以把数字转换为有意义的字符串。

利用这些数据结构包装error函数:

/*
 * s1 为字符串格式
 * s2 为字符串格式中的占位
 */
void error(char *s1, char *s2)
{
    extern int errno, sys_nerr;
    extern char *sys_errlist[], *progname;

    if (progname) {
        fprintf(stderr, "%s: ", progname);
    }
    fprintf(stderr, s1, s2);
    if (errno > 0 && errno < sys_nerr) {
        fprintf(stderr, " (%s)", sys_errlist[errno]);
    }
    fprintf(stderr, "\n");
    exit(1);
}

/*
 * 使用实例
 * 比如一个复制程序 cp
 * 使用方式为: cp oldfile newfile
 */
progname = argv[0];
if (argc != 3) {
    error("Usage: %s from to", progname);
}
if ( (f1 = open(argv[1], O_RDONLY, 0)) == -1) {
    error("can't open %s", argv[1]);
}
if ( (f2 = open(argv[2], O_CREAT, 0)) == -1) {
    error("can't create %s", argv[2]);
}

errno初始为0,并且总是小于sys_nerr。在运行正常时它不会被重置为0,所以,如果程序想要继续执行,就必须在每次产生错误后把它重置为0。

使用标准C处理错误信息

当调用一些函数请求操作系统执行一些功能(如打开文件),如果出现错误,操作系统通过设置外部整形变量errno进行错误代码报告,strerror函数把错误代码作为参数并返回一个指向用于描述错误的字符串的指针。

#include <string.h>

char *strerror(int error_number);

csapp中对系统调用做的错误处理

csapp中通过定义一系列错误包装函数,它们能够保证代码简洁,而又完成错误检查。

1. 定义错误报告函数(error-reporting funciton)

void unix_error(char *msg)
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}

2. 使用错误包装函数(error-handling wrapper)

对于一个给定的基本函数foo,定义一个具有相同参数但第一个字母大写的包装函数Foo。包装函数调用基本函数,检查错误,如果包装函数发现了错误,那么它就打印一条信息并终止进程,否则,它返回调用者。如果没有发生错误,包装函数的行为与基本函数完全一样。

比如给定fork的包装函数:

pid_t Fork(void)
{
    pid_t pid;

    if ( (pid = fork()) < 0) {
        unix_error("Fork error");
    }

    return pid;
}

之后对fork函数的调用就缩减为1行,而不用在做错误处理。

pid = Fork();