《UNIX网络编程 卷2:进程间通信(第2版)》——1.6 出错处理:包裹函数

简介: 在现实程序中,我们必须检查每个函数调用是否返回错误。由于碰到错误时终止程序执行是个惯例,因此我们可以通过定义包裹函数(wrapper function)来缩短程序的长度。包裹函数执行实际的函数调用,测试其返回值,并在碰到错误时终止进程。

本节书摘来自异步社区《UNIX网络编程 卷2:进程间通信(第2版)》一书中的第1章,第1.6节,作者:【美】W. Richard Stevens著,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.6 出错处理:包裹函数

在现实程序中,我们必须检查每个函数调用是否返回错误。由于碰到错误时终止程序执行是个惯例,因此我们可以通过定义包裹函数(wrapper function)来缩短程序的长度。包裹函数执行实际的函数调用,测试其返回值,并在碰到错误时终止进程。我们使用的命名约定是将函数名第一个字母改为大写字母,例如:

Sem_post(ptr);
图1-7定义了这个包裹函数。

screenshot

每当你遇到一个以大写字母打头的函数名时,它就是我们所说的包裹函数。它调用一个名字相同但以相应小写字母开头的实际函数。当碰到错误时,包裹函数总是在输出一个出错消息后终止。

我们在讲解书中提供的源代码时,所指代的总是被调用的最低层函数(例如sem_post),而不是包裹函数(例如Sem_post)。类似地,书后的索引也总是指代被调用的最低层函数,而不是指代包裹函数。

刚刚展示的源代码格式全书都在使用。每一非空行都被编号。代码的正文说明部分的左边标有起始与结束的行号。有的段落开始处含有一个醒目的简短标题,概述本段代码的内容。

源代码片段起始与结束处的水平划线标出了该片段所在源代码文件名,本例中就是lib目录下的wrapunix.c文件。既然本书所有例子的源代码都可免费获得(见前言),你就可以凭这个文件名找到相应的文件。阅读本书的过程中,编译、运行并修改这些程序是学习进程间通信概念的好方法。
尽管包裹函数不见得如何节省代码量,当在第7章中讨论线程时,我们会发现线程函数出错时并不设置标准的Unix errno变量;相反,本该设置errno的值改由线程函数作为其返回值返回调用者。这意味着我们每次调用任意一个线程函数时,都得分配一个变量来保存函数返回值,然后在调用我们的err_sys函数(图C-4)前,把errno设置成所保存的值。为避免源代码中到处出现花括弧,我们可以使用C语言的逗号运算符,把给errno赋值与调用err_sys组合成单个语句,如下所示:

int n; 

if ( (n = pthread_mutex_lock(&ndone_mutex)) != 0)
    errno = n, err_sys("pthread_mutex_lock error");

另一种办法是定义一个新的出错处理函数,它需要的另一个参数是系统的错误号①。但是我们可以将这段代码简化得更容易些:

Pthread_mutex_lock(&ndone_mutex);
其前提是定义自己的包裹函数,如图1-8所示。

screenshot

仔细推敲编码,我们可改用宏代替函数,从而稍稍提高运行效率,不过即使有过的话,包裹函数也很少是程序性能的瓶颈所在。

选择将函数名的第一个字母大写是一种较折中的方法。还有许多其他方法:例如用e作为函数名的前缀(如[Kernighan and Pike 1984]第184页所示),或者用_e作为函数名的后缀等。同样提供确实在调用某个其他函数的可视化指示,我们的方法看来是最少分散人们的注意力的。

这种技巧还有助于检查那些其错误返回值通常被忽略的函数,例如close和pthread_mutex_lock。
本书后面的例子中我们将普遍使用包裹函数,除非必须检查某个确定的错误并处理它(而不是终止进程)。我们并不给出所有包裹函数的源代码,但它们是免费可得的(见前言)。

Unix errno值
每当在一个Unix函数中发生错误时,全局变量errno将被设置成一个指示错误类型的正数,函数本身则通常返回-1。我们的err_sys函数检查errno的值并输出相应的出错消息,例如,errno的值等于EAGAIN时的出错消息为“Resource temporarily unavailable”(资源暂时不可用)。

errno的值只在某个函数发生错误时设置。如果该函数不返回错误,errno的值就无定义。所有正的错误值都是常值,具有以E打头的全部为大写字母的名字,通常定义在头文件中。没有值为0的错误。

在多线程环境中,每个线程必须有自己的errno变量。提供一个局限于线程的errno变量的隐式请求是自动处理的,不过通常需要告诉编译器所编译的程序是可重入的。给编译器指定类似-D_REENTRANT或-D_POSIX_C_SOURCE=199506L这样的命令行选项是较典型的方法。头文件往往把errno定义成一个宏,当常值_REENTRANT有定义时,该宏就扩展成一个函数,由它访问errno变量的某个局限于线程的副本。

全书使用类似“mq_send函数返回EMSGSIZE错误”的用语来简略地表示这样的意思:该函数返回一个错误(典型情况是返回值为-1),并且在errno中设置了指定的常值。

相关文章
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
|
23天前
|
机器学习/深度学习 编解码
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
本文介绍了九种常用的神经网络激活函数:Sigmoid、tanh、ReLU、ReLU6、Leaky ReLU、ELU、Swish、Mish和Softmax,包括它们的定义、图像、优缺点以及在深度学习中的应用和代码实现。
99 0
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
|
29天前
|
机器学习/深度学习 数据可视化 算法
激活函数与神经网络------带你迅速了解sigmoid,tanh,ReLU等激活函数!!!
激活函数与神经网络------带你迅速了解sigmoid,tanh,ReLU等激活函数!!!
|
2月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
2月前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
3月前
|
数据采集 量子技术 双11
【2023 年第十三届 MathorCup 高校数学建模挑战赛】C 题 电商物流网络包裹应急调运与结构优化问题 建模方案及代码实现
本文提供了2023年第十三届MathorCup高校数学建模挑战赛C题的详细建模方案及代码实现,针对电商物流网络中的包裹应急调运与结构优化问题,提出了包括时间序列分析在内的多种数学模型,并探讨了物流网络的鲁棒性。
59 2
【2023 年第十三届 MathorCup 高校数学建模挑战赛】C 题 电商物流网络包裹应急调运与结构优化问题 建模方案及代码实现
|
3月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
3月前
|
机器学习/深度学习 算法
神经网络中激活函数的重要性
【8月更文挑战第23天】
24 0
|
3月前
|
监控
【网络编程】poll函数
【网络编程】poll函数
24 0
|
3月前
|
监控
【网络编程】select函数
【网络编程】select函数
55 0