逍遥飞狐多媒体作坊 译
上一页
下一页
许多GNU
C库中的函数都监测和报告错误的情况,而且有时候你的程序需要检查它们以得到错误的情形。比如说:你打开了一个输入文件,你必须检查这个文件是否正确的打开了,而且在你调用一个库函数出错的时候打印出错信息或采取其他的措施。
这一章描述错误报告功能是怎么工作的。要使用这个功能的话,你的程序必须包含头文件“errno.h”。
许多库函数返回一个特殊的值说明他们的失败。这个特殊值典型的有-1、一个空指针、或者一个定义得像EOF这样的常量。但是这样的返回只是告诉你错误发生了。要找到这是什么样的错误,你必须查看变量errno中存放的错误代码。这个变量在头文件“errno.h”中定义。
变量:volatile interrno
变量errno包括了系统的错误号,你可以改变它的值。
当errno描述成volatile,它将可能被一个信号处理者同步的改变;参见《定义信号处理者》。一个写好的信号处理者能够存入或恢复errno的值,所以处理你编写信号处理者的时候之外一般你基本不用担心这个。
程序器启动的似乎errno的初始值是零。许多库函数在遭遇一些确定的错误以后将保证设置它为某个确定的非零值。当这些函数运行成功的时候它将不会修改errno的值;因此,当一个成功的调用以后errno的值不必要是零,而且你不能用errno来判断一次调用是否失败了。正确的做法是检查每个函数报道的结果,如果调用失败了,再来检查errno。
一些库函数会把errno设置为一个非零值来作为调用一个其他的可能出错的库函数的返回结果。你必须假定任何库函数都可能改变errno。
兼容性提示:ANSI
C把errno指定为一个比变量强的“可变左值”,允许它像宏一样实现。比如说:它可以扩展得包括一个函数调用:就像*
_errno()。实际上,那就是它自己在GNU系统上的本质。在非GNU系统上的GNU库中,就算不论怎样也是这样的。
这里有一些库函数,就像sqrt和atan,在万一出错的时候也能返回一个合法的值。对于这样的函数,如果你要检查是否发生了错误,推荐的手段就是在调用函数之前将errno设置为0,然后函数返回以后再检查它的值。
所有的错误代码都有一个符号名字;它们都是在“errno.h”中定义的宏。这些名字都由一个“E”开始后面跟着一个大写字母或数字;你必须把这些名字考虑成保留字,参见《保留字》
所有的错误代码都是互不相同的正整数。(比如:既然这些值是不同的,你就可以把它们用作switch结构的标签。)你不能对这些有特定值的符号常量假定其他的值。
errno的值并不仅限于这些宏,所以有些库函数在其他情形下会返回一些其他的错误码。当然手册列出的这些特定的库函数的这些特定的值是有另外的含义的
在非GNU的系统中,将一个无效的指针作为参数许多系统调用将返回EFAULT。
显然这结果会在在你的程序有bug的时候发生,而且该情况不会在GNU系统中发生,为节省篇幅,我们就不在进行单独的函数的描述的时候提及EFAULT。
所有的错误代码都在头文见“errno.h”中定义,它们所有的展开以后都是整数常量。这里的有些错误在GNU的系统中不会发生,但是在其他系统中使用GNU库的时候会出现。
宏:intEPERM
操作不被允许;只有文件(或其他资源)或进程的主人才能有特权作这个操作。
宏:intENOENT
没有这样的文件或目录。这是一个普通文件上的“文件不存在”错误,文件不在提到的目录表中。
宏:intESRCH
没有进程符合指定的进程号。
宏:intEINTR
函数调用中断;一个异步信号出现而且阻止调用的执行。当出现这样的情况时,你必须试着重新调用。
你可以选择一个函数来在处理完信号以后恢复,这样比出现RINTR的失败要好,《简单的信号中断》。
宏:intEIO
输入输出错误;通常是物理的读写错。
宏:intENXIO
没有这样的设备或地址。通常,这意味着一个文件描述的设备并没有正确安装,系统不能为它找到正确的设备驱动程序。
宏:intE2BIG
参数列表太长;当使用exec函数中的某一个执行新程序时传递的参数占用了太多的内存空间。这种情况不会出现在GNU的系统中。
宏:intENOEXEC
无效的可执行文件格式。这种情况由exec函数检测出来;《执行一个文件》
宏:intEBADF
错误的文件描述符;比如说,在一个已经关闭的文件描述符上作IO操作或读取一个写打开的描述符(反之亦然)。
宏:intECHILD
没有子进程。这个错误出现在想要作操纵子进程的操作的时候,已经没有任何可以操纵的子进程了。
宏:intEDEADLK
死锁避免;分配系统资源的时候将可能导致死锁的情况,比如说,参见《文件锁定》
宏:intENOMEM
没有可用的内存。系统不能分配更多的虚拟内存因为它的容量已经满了。
宏:intEACCES
许可权被封锁;文件的许可权不允许所尝试的操作。
宏:intEFAULT
错误地址;检测到一个无效指针。
宏:intENOTBLK
给出的这种方式需要块特殊文件而这个文件不是。比如说:试图把一个普通文件当文件系统挂在unix系统中将出现这个错误。
宏:intEBUSY
资源忙;一个正在使用的系统资源不能被共享。比如说,你需要删除的文件是一个挂在系统中的文件系统,你将得到这个错误。
宏:intEEXIST
文件已存在;应该对新文件操作的时候这个文件已经明确存在于目录表中。
宏:intEXDEV
尝试跨文件系统作文件链接。
宏:intENODEV
在需要进去日指明设备类别的函数中给出了错误的设备类型。
宏:intENOTDIR
在只能处理目录的时候给出的是文件而不是目录。
宏:intEISDIR
是一个目录文件;尝试打开一个目录文件来作写操作时就会发生这个错误。
宏:intEINVAL
无效的参数。用来指出传递给库函数参数时候的各种问题。
宏:intENFILE
整个系统中打开了太多不同的文件。注意许多连结通道都会被计算成一个打开的文件;参见《连结通道》
宏:intEMFILE
当前进程已经打开了太多文件而且不能再打开了。
宏:intENOTTY
不适当的IO控制操作,比如说在一个普通文件上试图设置终端模式。
宏:intETXTBSY
一个打算运行的文件当前正被写打开,或要写打开的文件当前正在运行。(这个名字象征着“文本文件忙”。)在GNU的系统中这不是一个错误;可能的情况下文本文件将被复制。
宏:intEFBIG
文件太大。文件的大小超过了系统的允许。
宏:intENOSPC
设备上没有空间;因为磁盘满而造成写操作失败。
宏:intESPIPE
错误的查找操作(比如在管道中)。
宏:intEROFS
在一个只读的文件系统中试图修改一个文件。
宏:intEMLINK
太多的连接;单个文件的连结数太大。
宏:intEPIPE
断掉的管道;在管道的另一边没有进程来读取。所有库函数在返回这个错误代码的时候都会产生一个SIGPIPE信号;如果不处理或阻止的话这个信号将终止程序。如此,要是不处理或阻止SIGPIPE的话你的程序实际上看不到EPIPE。
宏:intEDOM
域错误;当数学函数得到的参数值不在函数允许范围内的时候出现这个错误。
宏:intERANGE
范围错误;当数学函数的结果因上溢或下溢而无法描述的时候出现此错误。
宏:intEAGAIN
资源零是不可用;如果你稍候在作此调用或许会成功。只有fork会因为这个原因返回EAGAIN错误码。
宏:intEWOULDBLOCK
在一个非块格式的目标上作需要块的操作。
兼容性提示:在4.4BSD和GNU中,EWOULDBLOCK和EAGAIN是一样的。早期版本的BSD(参见《伯克利UNIX》)使用两个不同的代码,用EWOULDBLOCK来表示需要块的操作,而EAGAIN表示其他种类的错误。
宏:intEINPROGRESS
在一个选择为非块型的目标上的初始化操作不能立即完成。
宏:intEALREADY
在一个选择为非块型的目标上的一个操作正在进行。
宏:intENOTSOCK
当需要一个套接字的时候指定的文件不是套接字。
宏:intEDESTADDRREQ
在套接字操作中没有指定目标地址。
宏:intEMSGSIZE
送往套接字的消息的大小操作的支持的最大尺寸。
宏:intEPROTOTYPE
所要求的通信协议不支持套接字的种类。
宏:intENOPROTOOPT
你指定的套接字操作不能被使用的套接字的协议所识别,参见《套接字操作》
宏:intEPROTONOSUPPORT
套接字域不支持请求的通信协议。参见《创建一个套接字》
宏:intESOCKTNOSUPPORT
套接字类型不被支持。
宏:intEOPNOTSUPP
你请求的操作不被支持。一些套接字函数不被所有类型的套接字识别,而且有些不能在所有的通信协议上实现。
宏:intEPFNOSUPPORT
你请求的套接字协议组不被支持。
宏:intEAFNOSUPPORT
一个套接字指定的地址组不被支持;它和套接字使用的协议不一致。参见《套接字》
宏:intEADDRINUSE
请求的套接字地址正在使用。参见《套接字地址》
宏:intEADDRNOTAVAIL
请求的套接字地址已经不可用;比如说,你试着把一个和本机名字不一样的名字给套接字。参见《套接字地址》
宏:intENETDOWN
由于网络不通造成一个套接字操作失败。
宏:intENETUNREACH
由于子网上的远程主机不可到达造成一个套接字操作失败。
宏:intENETRESET
由于远程主机崩溃导致网络连接复位。
宏:intECONNABORTED
一个网络连接被本地中断。
宏:intECONNRESET
由于外界控制本地主机的原因造成一个网络连接关闭。比如远程的重启动。
宏:intENOBUFS
IO操作的内核缓冲区都在使用。
宏:intEISCONN
你试图连接一个已经连接了的套接字。参见《创建一个连接》。
宏:intENOTCONN
套接字没有连接任何东西。你得到这个错误的原因是你试图通过一个套接字传送数据,但是却没有先指定数据的目标。
宏:intESHUTDOWN
套接字已经被关闭。
宏:intETIMEDOUT
套接字操作在指定的超时时间间隔内没有收到回音。
宏:intECONNREFUSED
远程主机拒绝建立网络连接。(通常是因为它并不运行请求的这种服务)
宏:intELOOP
在查找文件名的时候遇到了许多指向它的符号连接。这个通常用来指出一个符号连接的环路。
宏:intENAMETOOLONG
文件名太长(比PATH_MAX还要长;参见《文件系统容量限制》)货主机名字太长(在gethostname或sethostname中;参见《主机身份》
宏:intEHOSTDOWN
请求网络连接的远程主机停机。
宏:intEHOSTUNREACH
请求网络连接的远程主机不可到达。
宏:intENOTEMPTY
在需要空目录的时候目录非空。通常这个错误出现在你准备删除一个目录的时候。
宏:intEUSERS
文件配额系统由于太多用户而拒绝。
宏:intEDQUOT
用户的磁盘限额已经超过。
宏:intESTALE
过期的NFS文件句柄。这里只是了NFS系统的内部混乱而且需要对服务器主机的文件系统重新整理。修理这种问题通常需要在本地主机上卸下再重新挂上此NFS文件系统。
宏:intEREMOTE
一个使用文件名来通过NFS加载远程文件系统的时候此文件名已经指定给一个加载好的文件了。(这只是在某些操作系统上的错误,但是我们希望在GNU系统上能运行正常,使这个错误代码不可能实现)
宏:intENOLCK
没有可用的锁定。由文件锁定功能使用;参见《文件锁》
宏:intENOSYS
功能没有实现。一些功能包括命令和选项的定义可能没有被所有的实现所支持,也就是你请求这些没有被支持的功能的时候你会得到这一类的错误。
宏:intED
有经验的用户将知道错误是什么。
宏:intEGRATUITOUS
这个错误信息没有用处。
库里的函数和变量都设计得使你的程序在调用库失败的时候容易的使用自定义的错误信息报告格式。函数strerror和perror将按给定的错误代码给你标准的错误信息;变量program_invocation_short_name将使你在遇到错误的时候方便的访问你的程序名。
函数:char *strerror(interrnum)
strerr函数映射由参数errnum表示的错误代码(参见《检查错误》)到错误信息字符串。返回值是一个字符串指针。
通常errnum的值从变量errno中来。
你不能修改strerroe返回的字符串。同样,如果你后来又调用了strerror,这个字符串将会被覆盖。但是这里可以保证在你之后没有任何库函数调用strerror。
这个函数在“string.h”中声明。
函数voidperror(const char *message)
此函数将错误信息打印到流stderr。查看节《标准流》
如果你调用perroe的message不是一个空指针就是一个空字符串,perror将把这个消息打印到相应的errno中,在后面增加一个空行。
如果你提供一个非空的message参数,prror将把这个字符串作为它的输出的前缀。它将增加一个冒号和空格字符来分开message中对应errno的错误字符串。
函数perror在stdio.h中描述。
sterror和perror对给定的错误代码提供严格一致的信息,就是系统下都一致的准确文本。在GNU系统中,这个信息是相当短的;这里没有多行的信息或内嵌的新行。每个信息都由一个大写字母开始而且没有结束的标点符号。
兼容性提示:函数strerror是ANSI的新定义,许多老的C系统仍然不支持这个函数。
许多不从终端读取输入的程序都设计成如果系统调用错误就退出。而且约定,这样一个程序产生的错误信息都由这个程序的名字开始,没有目录名。你可以从变量program_invocation_short_name中找到这个名字;完整的文件名存放在变量program_invocation_name中。
变量:char *program_invocation_name
这个变量的值是用来调用产生当前进程的程序的名字。它和argv[0]一样。
变量:char *program_invocation_short_name
这个变量的值是用来调用产生当前进程的程序的名字,但是去掉了目录名。(就是说,这即便是program_invocation_name,也是减掉了一大半的。)
program_invocation_name和program_invocation_short_name都是系统在调用main之前设定的。
兼容性提示:这两个变量都是GNU的扩展,如果你想让你的程序能在非GNU的函数库上工作,你必须在main中存下argv[0]的值,并且你自己去掉目录名。我们增加这个扩展的目的是使写不需要外面的main协助的子程序也能自己进行错误报告。
这里有一个例子显示如如何恰当的处理打开一个文件的错误。函数open_sesame尝试打开一个指定名字的文件并且在成功的情况下返回一个流。库函数fopen在因为某些原因打不开一个文件的时候返回一个空指针。在这样的情形下,open_message使用strerror函数构造一个错误信息,然后终止程序。如果我们想在调用其他库函数的时候忽略strerror的错误代码,我们必须把原来的error
code为一个本地的内部变量,因为其他库函数可能会在其间覆盖errno。
#include#include #include #include FILE * open_sesame (char *name) { FILE *stream; errno = 0; stream = fopen (name, "r"); if (!stream) { fprintf (stderr, "%s: Couldn't open file %s; %s\n", program_invocation_short_name, name, strerror (errno)); exit (EXIT_FAILURE); } else return stream; }
逍遥飞狐
http://www.linuxforum.net/forum/printthread.php?Cat=&Board=program&main=31537&type=post