8.3 系统调用错误处理

当 Unix 系统级函数遇到错误时,它们通常会返回 —1,并设置全局整数变量 errno 来表示什么出错了。程序员应该总是检査错误,但是不幸的是,许多人都忽略了错误检查,因为它使代码变得臃肿,而且难以读懂。比如,下面是我们调用 Unix fork 函数时会如何检査错误:

if ((pid = fork()) < 0) {
    fprintf(stderr, "fork error: %s\n", strerror(errno));
    exit(0);
}

strerror 函数返回一个文本串,描述了和某个 errno 值相关联的错误。通过定义下面的错误报告函数,我们能够在某种程度上简化这个代码:

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

给定这个函数,我们对 fork 的调用从 4 行缩减到 2 行:

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

通过使用错误处理包装函数,我们可以更进一步地简化代码,Stevens 在【110】中首先提出了这种方法。对于一个给定的基本函数 foo,我们定义一个具有相同参数的包装函数 Foo,但是第一个字母大写了。包装函数调用基本函数,检査错误,如果有任何问题就终止。比如,下面是 fork 函数的错误处理包装函数:

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

给定这个包装函数,我们对 fork 的调用就缩减为 1 行:

pid = Fork();

我们将在本书剩余的部分中都使用错误处理包装函数。它们能够保持代码示例简洁,而又不会给你错误的假象,认为允许忽略错误检査。注意,当在本书中谈到系统级函数时,我们总是用它们的小写字母的基本名字来引用它们,而不是用它们大写的包装函数名来引用。

关于 Unix 错误处理以及本书中使用的错误处理包装函数的讨论,请参见附录 A。包装函数定义在一个叫做 csapp.c 的文件中,它们的原型定义在一个叫做 csapp.h 的头文件中;可以从 CS:APP 网站上在线地得到这些代码。

最后更新于