Linu移植随笔:由ts_config:Success想到的 | 迟思堂工作室
A-A+

Linu移植随笔:由ts_config:Success想到的

2014-08-30 15:42 嵌入式Linux 暂无评论 阅读 3,072 次

背景:
其时正在搞qtopia-2.2.0,结果花费大量时间却收获甚少。本文只是以Tslib为背景介绍一下一些程序的出错处理。不涉及具体的移植、操作等等。
Tslib校准的测试如下:

# ./ts_calibrate
No raw modules loaded.
ts_config: Success

这个“错误”信息来自./tests/ts_calibrate.c文件第215行(注:./表示Tsllib所在的目录,下同):

if (ts_config(ts)) {
perror("ts_config");
exit(1);
}

当ts_config函数返回值为非零时,打印出错信息,并退出。
 
ts_config函数在./src/ts_config.c文件第91行:

if (ts->list_raw == NULL) {
ts_error("No raw modules loaded.n");
ret = -1;
}

它调用ts_error函数打印“No raw modules loaded.”,返回非零值。
tslib打印出错信息比较经典,有必要在这里重现一下(注意函数指针的用法):

static int stderrfn(const char *fmt, va_list ap)
{
return vfprintf(stderr, fmt, ap);
}
/*
* Change this hook to point to your custom error handling function.
*/
int (*ts_error_fn)(const char *fmt, va_list ap) = stderrfn;
int ts_error(const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = ts_error_fn(fmt, ap);
va_end(ap);
return ret;
}

罪魁祸首的就是这句:
perror("ts_config");
perror的手册描述如下:
The  routine  perror() produces a message on the standard error output,describing the last error encountered during a  call  to  a  system  or library  function.
网上有的资料将“last error”翻译成“最后的错误(号)”,这不太正确,应该说是“上一次的错误(号)”。正如英文中的“last night”一样。
这里将错误分两种:“系统错误”与“自定义错误”。系统错误信息由perror或strerror来打印。其中strerror是将errno转换成可读的字符串。比如errno为2的信息为“No such file or directory”,当没有发生错误时,errno为0号,即“Success”。错误号及其对应的信息可以在errno.h头文件中找到,在我的系统中,实际在/usr/include/asm-generic目录下的errno-base.h和errno.h 文件中定义。
前面的perror("ts_config");打印了“ts_config: Success”,就是因为上一次的系统(库)调用没有发生错误,errno返回值为0,为0者,当然是“Success”。
下面以打开串口的函数作例子。

int open_port(int port)
{
int fd = -1;  /* File descriptor for the port, we return it. */
if (port < 1 || port > 4)
{
printf("Sorry, the port number must be 1~4.n");
return -1;
}
fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("Unable to open the port");
return -1;
}
return fd;
}

示例代码中有两种错误,自定义的错误,即串口号只能在1~4之间,超出范围即提示错误,这里只使用简单的printf语句;另一个是调用系统调用open时的错误处理,使用perror来输出错误信息。
如果调用open_port(6),则会提示如下信息:
Sorry, the port number must be 1~4.
如果串口号范围正常,但由于调用open时发生错误,比如下面的两个:
Unable to open the port: Permission denied
Unable to open the port: No such file or directory
一个是权限不够(因为我使用普通用户来执行),另一个是设备文件不存在(因为我将device改为一个不存在的串口设备)。
下面使用perror和strerro作一些小测试:

perror("This is not an error!");
printf("errno %d:%s nerrno %d:%sn", 0, strerror(0), 2, strerror(2));

打印如下信息:
This is not an error!: Success
errno 0:Success
errno 2:No such file or directory

出错处理对于程序来说非常重要,一个很不好的出错处理例子如下:

if (ret == -1)
printf("发生错误,请联系管理员");
else if (ret == -2)
printf("发生错误,请联系管理员");
else if (ret == -3)
printf("发生错误,请联系管理员");
else if (ret == -4)
printf("发生错误,请联系管理员");
else if (ret == -5)
printf("发生错误,请联系管理员");

就是说,不能将所有的错误都交给“管理员”来处理,“管理员”能力再强,也有忘记ret所对应的各种错误信息的时候。正如为函数、变量命名要使用一些有意义的词语一样,最好不要用“process_data()”来表示“处理数据”,因为这个范围太大了,还不如用一些更具体的名称,比如是过滤字符串中某个字符、解析字符串,等等。
一般地,在Linux中调用系统调用都应该判断一下返回值(通常返回-1表示发生错误),同时使用perror或strerro来打印错误信息。但是不要将“系统信息”和“自定义错误”搞混了。此外,错误信息也应该全面一些,在易出错处,不能只打印一行“出错了”就了事。这个范围同样也太大了。到底是什么错误呢?权限不足?文件不存在?……太多了。
我们写代码应该有责任心。不为他人,亦为自己。



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate




给我留言