几乎所有的系统函数和库函数在执行时都会通过返回特定的值来说明成功或出错。我们在调用它们后,必须马上对其返回值进行检测,如果调用出错则要进行相应的处理(一般是向终端输出错误信息并终止程序运行)。否则在今后程序出错时,如果通过调试去定位到该错误将会花费很长的时间。
当然也有某些系统调用从不失败(例如getpid()或_exit()等),在调用它们时可以不进行错误检测。
fd = open(pathname, flags, mode);
if(fd == -1){
/* code to handle the error */
}
...
if(close(fd) == -1){
/* code to handle the error */
}
**注意:**不同于UNIX系统函数和C库函数,POSIX标准下的函数成功执行时,函数返回“0”,出错时设定errno为特定的非0整数值。
当一个系统调用失败时,内核会根据错误类型将error设定一个特定的非负整数。头文件
为每一个可能的error值定义了一个以“E”开头的宏名,在错误处理函数中可以通过检测这个特定的宏名来判断错误类型。
cnt = read(fd, buf, numtybes);
if(cnt == -1){
if(errno == EINTR)
fprintf(stderr, "read was interrupted by a signal\n");
else{
/*some other error occurred */
}
}
当系统调用成功时,它并不会将error设定为0。因此,可能出现的一种情况是当前的系统函数成功执行了,但当前error值还是以前某次系统调用出错时设定的值,为此,在对error值进行检测以判定是哪种错误时,一定一定要先检查当前系统调用是否发生错误!
对待系统调用出错的常见处理方法是基于error值输出不同的错误提示消息。库函数perror()
和strerror()
提供类似的功能。
perror()
函数先打印由用户自定义的字符串后,接着输出error值对应的错误提示字符串。#include
void perror(const char *msg);
一个使用它的简单的例子:
fd = open(pathname, flags, mode);
if(fd == -1){
perror("open");
exit(EXIT_FAILURE);
}
strerror()
函数根据参数值返回特定的错误消息。#include
char *strerror(int errnum);
//返回与errnum相应的错误消息的指针
因为strerror函数返回的错误消息字符串所在的空间是静态分配的,所以当前的错误消息会被下一次strerror函数调用所覆盖。
如果传给它的参数errnum是非法的,那么它会返回“Unknown error nnn”,在某些实现上也可能返回NULL指针。
error_functions.h
,里面声明了7个错误处理函数:/*
Func Name : error_functions.h
Func Describe : Header file for error_functions.c.
*/
#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H
/* Error diagnostic routines */
void errMsg(const char *format, ...);
#ifdef __GNUC__
/* This macro stops 'gcc -Wall' complaining that "control reaches
end of non-void function" if we use the following functions to
terminate main() or some other non-void function. */
#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif
void errExit(const char *format, ...) NORETURN ;
void err_exit(const char *format, ...) NORETURN ;
void errExitEN(int errnum, const char *format, ...) NORETURN ;
void fatal(const char *format, ...) NORETURN ;
void usageErr(const char *format, ...) NORETURN ;
void cmdLineErr(const char *format, ...) NORETURN ;
#endif
exit()
函数或abort()
函数(如果环境变量EF_DUMPCORE设置为非空,则会调用该函数生成核转储文件供调试用)终止程序,格式同上。_exit()
函数代替exit()
函数和输出错误消息时不刷新标准输入输出缓存(stdio buffers),其余同errExit()
函数。其主要用于当一个进程创建的一个子进程出错需要终止时,得避免子进程刷新从父进程那继承过来的stdio缓存。/*此处不用errno的原因是errno其实代表一个宏调用,
这个宏调用会调用一个函数来返回其值,会影响运行效率。*/
int s;
s = pthread_creat(&thread, NULL, func, &arg);
if(s != 0)
errExitEN(s, "pthread_creat");
下面是它们的具体实现:
/*
Func Name : error_functions.c
Func Describe : Some standard error handling routines used by various programs.
*/
#include
#include "error_functions.h"
#include "tlpi_hdr.h"
#include "ename.c.inc" /* Defines ename and MAX_ENAME */
#ifdef __GNUC__ /* Prevent 'gcc -Wall' complaining */
__attribute__ ((__noreturn__)) /* if we call this function as last */
#endif /* statement in a non-void function */
static void
terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '\0')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
/* Diagnose 'errno' error by:
* outputting a string containing the error name (if available
in 'ename' array) corresponding to the value in 'err', along
with the corresponding error message from strerror(), and
* outputting the caller-supplied error message specified in
'format' and 'ap'. */
static void
outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
#if __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
#if __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
/* Display error message including 'errno' diagnostic, and
return to caller */
void
errMsg(const char *format, ...)
{
va_list argList;
int savedErrno;
savedErrno = errno; /* In case we change it here */
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
errno = savedErrno;
}
/* Display error message including 'errno' diagnostic, and
terminate the process */
void
errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Display error message including 'errno' diagnostic, and
terminate the process by calling _exit().
The relationship between this function and errExit() is analogous
to that between _exit(2) and exit(3): unlike errExit(), this
function does not flush stdout and calls _exit(2) to terminate the
process (rather than exit(3), which would cause exit handlers to be
invoked).
These differences make this function especially useful in a library
function that creates a child process that must then terminate
because of an error: the child must terminate without flushing
stdio buffers that were partially filled by the caller and without
invoking exit handlers that were established by the caller. */
void
err_exit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, FALSE, format, argList);
va_end(argList);
terminate(FALSE);
}
/* The following function does the same as errExit(), but expects
the error number in 'errnum' */
void
errExitEN(int errnum, const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errnum, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print an error message (without an 'errno' diagnostic) */
void
fatal(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(FALSE, 0, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print a command usage error message and terminate the process */
void
usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
/* Diagnose an error in command-line arguments and
terminate the process */
void
cmdLineErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Command-line usage error: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
此处ename是一个包含所有错误类型简称字符的数组,定义于ename.c.inc文件中。其用errno值作为索引存储对应的错误类型简称。同时由于不同内核版本即架构的区别,错误类型名称即数值也不同,所以此处提供了一个脚本Build_ename.sh用以产生ename.c.inc文件。
#!/bin/sh
#
# Create a new version of the file ename.c.inc by parsing symbolic
# error names defined in errno.h
#
echo '#include ' | cpp -dM |
sed -n -e '/#define *E/s/#define *//p' |sort -k2n |
awk '
BEGIN {
entries_per_line = 4
line_len = 68;
last = 0;
varname =" enames";
print "static char *ename[] = {";
line = " /* 0 */ \"\"";
}
{
if ($2 ~ /^E[A-Z0-9]*$/) { # These entries are sorted at top
synonym[$1] = $2;
} else {
while (last + 1 < $2) {
last++;
line = line ", ";
if (length(line ename) > line_len || last == 1) {
print line;
line = " /* " last " */ ";
line = sprintf(" /* %3d */ ", last);
}
line = line "\"" "\"" ;
}
last = $2;
ename = $1;
for (k in synonym)
if (synonym[k] == $1) ename = ename "/" k;
line = line ", ";
if (length(line ename) > line_len || last == 1) {
print line;
line = " /* " last " */ ";
line = sprintf(" /* %3d */ ", last);;
}
line = line "\"" ename "\"" ;
}
}
END {
print line;
print "};"
print "";
print "#define MAX_ENAME " last;
}
'
获取更多知识,请点击关注:
嵌入式Linux&ARM
CSDN博客
简书博客
知乎专栏