内核中各种出错函数处理的总结.docx
- 文档编号:9749701
- 上传时间:2023-02-06
- 格式:DOCX
- 页数:6
- 大小:19.09KB
内核中各种出错函数处理的总结.docx
《内核中各种出错函数处理的总结.docx》由会员分享,可在线阅读,更多相关《内核中各种出错函数处理的总结.docx(6页珍藏版)》请在冰豆网上搜索。
内核中各种出错函数处理的总结
1、许多的内核函数需要返回一个指针,但是函数的调用可能失败,一般我们处理这样的情形都是返回一个NULL指针,就像malloc或kmalloc在没有获得指定的空间申请时的返回值一样。
但是有时我们想知道导致函数失败的原因,但是返回NULL就显得信息不够。
因此有些函数返回一个实际的错误编码以便对引起错误的原因做一些处理。
很多内核接口通过把错误值编码到一个指针值中来返回错误信息。
当处理这样的函数时,判断是否成功调用就不能是简单的和NULL进行比较。
为了方便使用这样的类型接口,2.6的内核在linux/err.h中实现了三个内联函数:
#defineMAX_ERRNO4095
#ifndef__ASSEMBLY__
#defineIS_ERR_VALUE(x)unlikely((x)>=(unsignedlong)-MAX_ERRNO)
staticinlinevoid*ERR_PTR(longerror)
{
return(void*)error;
}
staticinlinelongPTR_ERR(constvoid*ptr)
{
return(long)ptr;
}
staticinlinelongIS_ERR(constvoid*ptr)
{
returnIS_ERR_VALUE((unsignedlong)ptr);
}
所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即ptr&0xfff==0
这样ptr的值不可能落在(0xfffff000,0xffffffff)之间,
而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsignedlong,
正好在(0xfffff000,0xffffffff)之间。
因此可以用
(unsignedlong)ptr>(unsignedlong)-1000L
来判断内核函数的返回值是一个有效的指针,还是一个出错代码。
像structclass*cls=class_create();这种语句,其中返回的指针值并不是kmalloc一样这么简单,只判断是否为NULL就可以了,内核是返回其错误值。
那么我怎么来判断它呢,总不能用if()来将每个错误例出来吧,这里我们的IS_ERR()宏就发挥作用了。
先看源代码,再讲原理,看看内核中的巧妙设计思路。
/*include/linux/err.h*/
staticinlinelong__must_checkIS_ERR(constvoid*ptr)
{
returnIS_ERR_VALUE((unsignedlong)ptr);
}
#defineIS_ERR_VALUE(x)unlikely((x)>=(unsignedlong)-MAX_ERRNO)
内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来。
所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即
ptr&0xfff==0
这样ptr的值不可能落在(0xfffff000,0xffffffff)之间,而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsignedlong,正好在(0xfffff000,0xffffffff)之间。
因此可以用
(unsignedlong)ptr>(unsignedlong)-1000L
也就等效于(x)>=(unsignedlong)-MAX_ERRNO
其中MAX_ERRNO为4095
来判断内核函数的返回值是一个有效的指针,还是一个出错代码。
涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,如果超过这个地址,则肯定是错误的。
而我们的错误码的值在内存中定义都是这样的(include/linux/errno.h):
......
#defineENOLCK77/*Norecordlocksavailable*/
#defineENOSYS78/*Functionnotimplemented*/
#defineENOMSG80/*Nomessageofdesiredtype*/
#defineEIDRM81/*Identifierremoved*/
#defineENOSR82/*Outofstreamsresources*/
#defineETIME83/*Timerexpired*/
#defineEBADMSG84/*Notadatamessage*/
#defineEPROTO85/*Protocolerror*/
#defineENODATA86/*Nodataavailable*/
#defineENOSTR87/*Devicenotastream*/.
........
现在应该知道为什么我写返回错误码的时候也加个负号如-ENOSYS这样子了。
至于PTR_ERR(),ERR_PTR(),只是强制转换以下而已,源代码如下(include/linux/err.h):
staticinlinevoid*__must_checkERR_PTR(longerror)
{
return(void*)error;
}
staticinlinelong__must_checkPTR_ERR(constvoid*ptr)
{
return(long)ptr;
}
所以像上面的cls例子可以这样写:
structclass*cls=class_create(....);
if(IS_ERR(cls))
{
ret=PTR_ERR(cls);
returnret;
}
2、voidperror(constchar*s);
perror()用来将上一个函数发生错误的原因输出到标准设备(stderr)。
参数s所指的字符串会先打印出,后面再加上错误原因字符串。
此错误原因依照全局变量error的值来决定要输出的字符串。
在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。
当你调用"某些"函数出错时,该函数已经重新设置了error的值。
perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。
例子:
#include
intmain(void)
{ FILE*fp;
fp=fopen("/root/noexitfile","r+");
if(NULL==fp)
{
perror("/root/noexitfile");
}
return0;
}
输出结果:
/root/noexitfile:
Nosuchfileordirectory
3、fprintf函数也可以打印错误的信息!
见下面解析(摘自网上的一些好文章,并做了一些修改):
intfprintf(FILE*restrictfp,constchar*restrictformat,...);
所谓流,通常是指程序输入或输出的一个连续的字节序列,设备(例如鼠标、键盘、磁盘、屏幕、调制解调器和打印机)的输入和输出都是用流来处理的,在C语言中,所有的流均以文件的形式出现——不一定是物理磁盘文件,还可以是对应于某个输入/输出源的逻辑文件。
C语言提供了5种标准的流,你的程序在任何时候都可以使用它们,并且不必打开或关闭它们。
以下列出了这5种标准的流。
名称
描述
例子
stdin
stdout
stderr
stdprn
stdaux
标准输入
标准输出
标准错误
标准打印机
标准串行设备
键盘
屏幕
屏幕
LPT1端口
COM1端口
其中,stdprn和stdaux并不总是预先定义好的,因为LPT1和COM1端口在某些操作系统中是没有意义的,而stdin,stdout和stderr总是预先定义好的。
此外,stdin并不一定来自键盘,stdout也并不一定显示在屏幕上,它们都可以重定向到磁盘文件或其它设备上。
我们在头文件stdio.h中可以找到stdin,stdout和stderr的定义如下:
/*Standardstreams.*/
externstruct_IO_FILE*stdin;/*Standardinputstream.*/
externstruct_IO_FILE*stdout;/*Standardoutputstream.*/
externstruct_IO_FILE*stderr;/*Standarderroroutputstream.*/
在使用fprintf()函数时,通常我们可以将第一个参数设为stdout或者stderr,打印出错调试信息的时候则推荐使用stderr而不是stdout,这是一种惯例,同时也由于内核在处理stdout和stderr时的优先级不一样,后者的优先级要高一些,因此有时候如果程序异常退出时,stderr能得到输出,而stdout就不行。
printf(...)实际上相当于fprintf(stdout,...),这也是为什么我们不推荐使用它的原因。
在输出调试信息的时候,我们推荐使用fprintf(stderr,…),或者使用某个指定的文件流fprintf(some_stream,…)。
例子:
#include
voidmain()
{
fprintf(stdout,"thisisfirst!
\n");
fprintf(stderr,"thisissecond!
\n");
printf("thisisthird!
\n");
}
stdout标准输出、stderr标准错误输出,二者默认向屏幕输出。
如果重定向输出到磁盘文件,则stdout输出到该文件,而stderr仍输出到屏幕。
stderr是作为程序运行中的错误显示出来的,若要把它重定向到磁盘文件,需要运行如下命令:
#./fprint2>tmp.txt
thisisfirst!
thisisthird!
文件tmp.txt中的内容为:
thisissecond!
PS:
关于为什么"./fprint2>tmp.txt"中的2;在UNIX系统中,标准输入、标准输出、标准错误输出分别被定义为0、1、2。
附录:
各种格式符:
目前就这几种错误提示函数,以后若有再添加进去!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 内核 各种 出错 函数 处理 总结