在看内核源码的时分,经常会遇到IS_ERR,比方在linux/arch/arm/kernel/sys_arm.c中
print?
- asmlinkageintsys_execve(char__user*filenamei,char__user*__user*argv,
- char__user*__user*envp,structpt_regs*regs)
- {
- interror;
- char*filename;
- filename=getname(filenamei);
- error=PTR_ERR(filename);
- if(IS_ERR(filename))
- gotoout;
- error=do_execve(filename,argv,envp,regs);
- putname(filename);
- out:
- returnerror;
- }
IS_ERR宏界说在include/linux/err.h,如下所示:
print?
- #ifndef_LINUX_ERR_H
- #define_LINUX_ERR_H
- #include
- #include
- /*
- *Kernelpointershaveredundantinformation,sowecanusea
- *schemewherewecanreturneitheranerrorcodeoradentry
- *pointerwiththesamereturnvalue.
- *
- *Thisshouldbeaper-architecturething,toallowdifferent
- *errorandpointerdecisions.
- */
- #defineIS_ERR_VALUE(x)unlikely((x)>(unsignedlong)-1000L)
- staticinlinevoid*ERR_PTR(longerror)
- {
- return(void*)error;
- }
- staticinlinelongPTR_ERR(constvoid*ptr)
- {
- return(long)ptr;
- }
- staticinlinelongIS_ERR(constvoid*ptr)
- {
- returnIS_ERR_VALUE((unsignedlong)ptr);
- }
- #endif/*_LINUX_ERR_H*/
下面咱们就来具体分析一下这段代码,看看内核中的奇妙规划思路。
要想了解IS_ERR(),首要了解要内核空间。一切的驱动程序都是运转在内核空间,内核空间尽管很大,但总是有限的,而在这有限的空间中,其最终一个page是专门保存的,也便是说一般人不可能用到内核空间最终一个page的指针。换句话说,你在写设备驱动程序的过程中,涉及到的任何一个指针,必定有三种状况:
- 有用指针;
- NULL,空指针;
- 过错指针,或者说无效指针。
而所谓的过错指针便是指其现已抵达了最终一个page,即内核用最终一页捕捉过错。比方关于32bit的体系来说,内核空间最高地址0xffffffff,那么最终一个page便是指的0xfffff000~0xffffffff(假定4k一个page),这段地址是被保存的。内核空间为什么留出最终一个page?咱们知道一个page可能是4k,也可能是更多,比方8k,但至少它也是4k,所以留出一个page出来就可以让咱们把内核空间的指针来记载过错了。内核回来的指针一般是指向页面的鸿沟(4k鸿沟),即ptr & 0xfff == 0。假如你发现你的一个指针指向这个规模中的某个地址,那么你的代码必定犯错了。IS_ERR()便是判别指针是否有错,假如指针并不是指向最终一个page,那么没有问题;假如指针指向了最终一个page,那么阐明实际上这不是一个有用的指针,这个指针里保存的实际上是一种过错代码。而一般很常用的办法便是先用IS_ERR()来判别是否是过错,然后假如是,那么就调用PTR_ERR()来回来这个过错代码。因而,判别一个指针是不是有用的,可用如下的办法:
#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)
(unsigned long)-1000L 应该为 (unsigned long)-0x1000L!(由于 -0x1000 才是 0xFFFFF000),这应该是内核的一个bug吧!在2.6.30.4的内核中是这样界说的:
print?
- #defineMAX_ERRNO4095
- #defineIS_ERR_VALUE(x)unlikely((x)>=(unsignedlong)-MAX_ERRNO)
即判别是不是在(0xfffff000,0xffffffff)之间,因而,可以用IS_ERR()来判别内核函数的回来值是不是一个有用的指针。留意这儿用unlikely()的意图!
至于PTR_ERR(), ERR_PTR(),仅仅强制转化以下罢了。现在应该知道为什么我写回来过错码的时分也加个负号如 -ENOSYS这姿态了。而PTR_ERR()仅仅回来过错代码,也便是供给一个信息给调用者,假如你只需要知道是否犯错,而不在乎由于什么而犯错,那你当然不必调用PTR_ERR()了。
而咱们的过错码的值在内存中界说都是这样的(asm-generic/errno-base.h):
print?
- ……
- #defineEPERM1/*Operationnotpermitted*/
- #defineENOENT2/*Nosuchfileordirectory*/
- #defineESRCH3/*Nosuchprocess*/
- #defineEINTR4/*Interruptedsystemcall*/
- #defineEIO5/*I/Oerror*/
- #defineENXIO6/*Nosuchdeviceoraddress*/
- #defineE2BIG7/*Argumentlisttoolong*/
- #defineENOEXEC8/*Execformaterror*/
- #defineEBADF9/*Badfilenumber*/
- #defineECHILD10/*Nochildprocesses*/
- #defineEAGAIN11/*Tryagain*/
- #defineENOMEM12/*Outofmemory*/
- #defineEACCES13/*Permissiondenied*/
- #defineEFAULT14/*Badaddress*/
- #defineENOTBLK15/*Blockdevicerequired*/
- #defineEBUSY16/*Deviceorresourcebusy*/
- #defineEEXIST17/*Fileexists*/
- #defineEXDEV18/*Cross-devicelink*/
- #defineENODEV19/*Nosuchdevice*/
- #defineENOTDIR20/*Notadirectory*/
- #defineEISDIR21/*Isadirectory*/
- #defineEINVAL22/*Invalidargument*/
- #defineENFILE23/*Filetableoverflow*/
- #defineEMFILE24/*Toomanyopenfiles*/
- #defineENOTTY25/*Notatypewriter*/
- #defineETXTBSY26/*Textfilebusy*/
- #defineEFBIG27/*Filetoolarge*/
- #defineENOSPC28/*Nospaceleftondevice*/
- #defineESPIPE29/*Illegalseek*/
- #defineEROFS30/*Read-onlyfilesystem*/
- #defineEMLINK31/*Toomanylinks*/
- #defineEPIPE32/*Brokenpipe*/
- #defineEDOM33/*Mathargumentoutofdomainoffunc*/
- #defineERANGE34/*Mathresultnotrepresentable*/
- ……..
假如指针指向了最终一个page,那么阐明实际上这不是一个有用的指针。这个指针里保存的实际上是一种过错代码。而一般很常用的办法便是先用IS_ERR()来判别是否是过错,然后假如是,那么就调用PTR_ERR()来回来这个过错代码。