linux內(nèi)核中的IS_ERR
- 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;
- }
- #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(),首先理解要內(nèi)核空間。所有的驅(qū)動程序都是運行在內(nèi)核空間,內(nèi)核空間雖然很大,但總是有限的,而在這有限的空間中,其最后一個page是專門保留的,也就是說一般人不可能用到內(nèi)核空間最后一個page的指針。換句話說,你在寫設(shè)備驅(qū)動程序的過程中,涉及到的任何一個指針,必然有三種情況:
- 有效指針;
- NULL,空指針;
- 錯誤指針,或者說無效指針。
而所謂的錯誤指針就是指其已經(jīng)到達(dá)了最后一個page,即內(nèi)核用最后一頁捕捉錯誤。比如對于32bit的系統(tǒng)來說,內(nèi)核空間最高地址0xffffffff,那么最后一個page就是指的0xfffff000~0xffffffff(假設(shè)4k一個page),這段地址是被保留的。內(nèi)核空間為什么留出最后一個page?我們知道一個page可能是4k,也可能是更多,比如8k,但至少它也是4k,所以留出一個page出來就可以讓我們把內(nèi)核空間的指針來記錄錯誤了。內(nèi)核返回的指針一般是指向頁面的邊界(4k邊界),即ptr & 0xfff == 0。如果你發(fā)現(xiàn)你的一個指針指向這個范圍中的某個地址,那么你的代碼肯定出錯了。IS_ERR()就是判斷指針是否有錯,如果指針并不是指向最后一個page,那么沒有問題;如果指針指向了最后一個page,那么說明實際上這不是一個有效的指針,這個指針里保存的實際上是一種錯誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯誤,然后如果是,那么就調(diào)用PTR_ERR()來返回這個錯誤代碼。因此,判斷一個指針是不是有效的,可用如下的方式:
#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)
(unsigned long)-1000L 應(yīng)該為 (unsigned long)-0x1000L!(因為 -0x1000 才是 0xFFFFF000),這應(yīng)該是內(nèi)核的一個bug吧!在2.6.30.4的內(nèi)核中是這樣定義的:
- #defineMAX_ERRNO4095
- #defineIS_ERR_VALUE(x)unlikely((x)>=(unsignedlong)-MAX_ERRNO)
即判斷是不是在(0xfffff000,0xffffffff)之間,因此,可以用IS_ERR()來判斷內(nèi)核函數(shù)的返回值是不是一個有效的指針。注意這里用unlikely()的用意!
至于PTR_ERR(), ERR_PTR(),只是強制轉(zhuǎn)換以下而已。現(xiàn)在應(yīng)該知道為什么我寫返回錯誤碼的時候也加個負(fù)號如 -ENOSYS這樣子了。而PTR_ERR()只是返回錯誤代碼,也就是提供一個信息給調(diào)用者,如果你只需要知道是否出錯,而不在乎因為什么而出錯,那你當(dāng)然不用調(diào)用PTR_ERR()了。
而我們的錯誤碼的值在內(nèi)存中定義都是這樣的(asm-generic/errno-base.h):
- ......
- #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*/
- ........
評論