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

1.6 出错处理:包裹函数

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

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

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

我们在讲解书中提供的源代码时,所指代的总是被调用的最低层函数(例如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所示。

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

选择将函数名的第一个字母大写是一种较折中的方法。还有许多其他方法:例如用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中设置了指定的常值。

时间: 2024-10-24 14:32:42

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

《UNIX网络编程 卷2:进程间通信(第2版)》——第1章 简介 1.1 概述

第1章 简介 1.1 概述 IPC是进程间通信(interprocess communication)的简称.传统上该术语描述的是运行在某个操作系统之上的不同进程间各种消息传递(message passing)的方式.本书还讲述多种形式的同步(synchronization),因为像共享内存区这样的较新式的通信需要某种形式的同步参与运作. 在Unix操作系统过去30年的演变史中,消息传递历经了如下几个发展阶段. 管道(pipe,第4章)是第一个广泛使用的IPC形式,既可在程序中使用,也可从she

《UNIX网络编程 卷2:进程间通信(第2版)》——导读

**前言 **大多数重要的程序都涉及进程间通信(Interprocess Communication, IPC).这是受下述设计原则影响的自然结果:把应用程序设计为一组相互通信的小片断比将其设计为单个庞大的程序更好.从历史角度看,应用程序有如下几种构建方法. (1)用一个庞大的程序完成全部工作.程序的各部分可以实现为函数,函数之间通过参数.返回值和全局变量来交换信息. (2)使用多个程序,程序之间用某种形式的IPC进行通信.许多标准的Unix工具都是按这种风格设计的,它们使用shell管道(IP

《UNIX网络编程 卷2:进程间通信(第2版)》——1.9 小结

1.9 小结 IPC传统上是Unix中一个杂乱不堪的领域.虽然有了各种各样的解决办法,但没有一个是完美的.我们的讨论分成4个主要领域: (1)消息传递(管道.FIFO.消息队列): (2)同步(互斥锁.条件变量.读写锁.信号量): (3)共享内存区(匿名共享内存区.有名共享内存区): (4)过程调用(Solaris门.Sun RPC). 我们考虑单个进程中多个线程间的IPC以及多个进程间的IPC. 各种类型IPC的持续性可以是随进程持续的.随内核持续的或随文件系统持续的,这取决于IPC对象存在时

《UNIX网络编程 卷1:套接字联网API(第3版)》——2.6 TCP连接的建立和终止

2.6 TCP连接的建立和终止 为帮助大家理解connect.accept和close这3个函数并使用netstat程序调试TCP应用,我们必须了解TCP连接如何建立和终止,并掌握TCP的状态转换图. 2.6.1 三路握手建立一个TCP连接时会发生下述情形. (1)服务器必须准备好接受外来的连接.这通常通过调用socket.bind和listen这3个函数来完成,我们称之为被动打开(passive open). (2)客户通过调用connect发起主动打开(active open).这导致客户T

《UNIX网络编程 卷1:套接字联网API(第3版)》——导读

**前言**本书面向的读者是那些希望自己编写的程序能使用称为套接字(socket)的API进行彼此通信的人.有些读者可能已经非常熟悉套接字了,因为这个模型几乎已经成了网络编程的同义词,但有些读者可能仍需要从头开始学习.本书想达到的目标是向大家提供网络编程指导.这些内容不仅适用于专业人士,也适用于初学者:不仅适用于维护已有代码,也适用于开发新的网络应用程序:此外,还适用于那些只是想了解一下自己系统中网络组件的工作原理的人. 书中的所有示例都是在Unix系统上测试通过的真实的.可运行的代码.但是,考

《UNIX网络编程 卷1:套接字联网API(第3版)》——2.7 TIME_WAIT状态

2.7 TIME_WAIT状态 毫无疑问,TCP中有关网络编程最不容易理解的是它的TIME_WAIT状态.在图2-4中我们看到执行主动关闭的那端经历了这个状态.该端点停留在这个状态的持续时间是最长分节生命期(maximum segment lifetime,MSL)的两倍,有时候称之为2MSL. 任何TCP实现都必须为MSL选择一个值.RFC 1122[Braden 1989]的建议值是2分钟,不过源自Berkeley的实现传统上改用30秒这个值.这意味着TIME_WAIT状态的持续时间在1分钟

《UNIX网络编程 卷1:套接字联网API(第3版)》——2.3 用户数据报协议(UDP)

2.3 用户数据报协议(UDP) UDP是一个简单的传输层协议,在RFC 768[Postel 1980]中有详细说明.应用进程往一个UDP套接字写入一个消息,该消息随后被封装(encapsulating)到一个UDP数据报,该UDP数据报进而又被封装到一个IP数据报,然后发送到目的地.UDP不保证UDP数据报会到达其最终目的地,不保证各个数据报的先后顺序跨网络后保持不变,也不保证每个数据报只到达一次. 我们使用UDP进行网络编程所遇到的问题是它缺乏可靠性.如果一个数据报到达了其最终目的地,但是

《UNIX网络编程 卷1:套接字联网API(第3版)》——1.5 一个简单的时间获取服务器程序

1.5 一个简单的时间获取服务器程序 我们可以编写一个简单的TCP时间获取服务器程序,它和1.2节中的客户程序一道工作.图1-9给出了这个服务器程序,它使用了上一节中讲过的包裹函数. 创建TCP套接字10 TCP套接字的创建与客户程序相同. 把服务器的众所周知端口捆绑到套接字11~15 通过填写一个网际套接字地址结构并调用bind函数,服务器的众所周知端口(对于时间获取服务是13)被捆绑到所创建的套接字.我们指定IP地址为INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在

《UNIX网络编程 卷1:套接字联网API(第3版)》——1.3 协议无关性

1.3 协议无关性 图1-5中的程序是与IPv4协议相关的:我们分配并初始化一个sockaddr_in类型的结构,把该结构的协议族成员设置为AF_INET,并指定socket函数的第一个参数为AF_INET. 为了让图1-5中的程序能够在IPv6上运行,我们必须修改这段代码.图1-6所示的是一个能够在IPv6上运行的版本,其中改动之处用加粗的等宽字体突出显示. 我们只修改了程序的5行代码,得到的却是另一个与协议相关的程序:这回是与IPv6协议相关的.更好的做法是编写协议无关的程序.图11-11将