新聞中心

linux內(nèi)核中的IS_ERR

作者: 時(shí)間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
在看內(nèi)核源碼的時(shí)候,經(jīng)常會遇到IS_ERR,比如在linux/arch/arm/kernel/sys_arm.c中

本文引用地址:http://2s4d.com/article/201611/319992.htm
[plain]view plaincopy
print?
  1. asmlinkageintsys_execve(char__user*filenamei,char__user*__user*argv,
  2. char__user*__user*envp,structpt_regs*regs)
  3. {
  4. interror;
  5. char*filename;
  6. filename=getname(filenamei);
  7. error=PTR_ERR(filename);
  8. if(IS_ERR(filename))
  9. gotoout;
  10. error=do_execve(filename,argv,envp,regs);
  11. putname(filename);
  12. out:
  13. returnerror;
  14. }
IS_ERR宏定義在include/linux/err.h,如下所示:

[plain]view plaincopy
print?
  1. #ifndef_LINUX_ERR_H
  2. #define_LINUX_ERR_H
  3. #include
  4. #include
  5. /*
  6. *Kernelpointershaveredundantinformation,sowecanusea
  7. *schemewherewecanreturneitheranerrorcodeoradentry
  8. *pointerwiththesamereturnvalue.
  9. *
  10. *Thisshouldbeaper-architecturething,toallowdifferent
  11. *errorandpointerdecisions.
  12. */
  13. #defineIS_ERR_VALUE(x)unlikely((x)>(unsignedlong)-1000L)
  14. staticinlinevoid*ERR_PTR(longerror)
  15. {
  16. return(void*)error;
  17. }
  18. staticinlinelongPTR_ERR(constvoid*ptr)
  19. {
  20. return(long)ptr;
  21. }
  22. staticinlinelongIS_ERR(constvoid*ptr)
  23. {
  24. returnIS_ERR_VALUE((unsignedlong)ptr);
  25. }
  26. #endif/*_LINUX_ERR_H*/
下面我們就來具體分析一下這段代碼,看看內(nèi)核中的巧妙設(shè)計(jì)思路。

要想明白IS_ERR(),首先理解要內(nèi)核空間。所有的驅(qū)動(dòng)程序都是運(yùn)行在內(nèi)核空間,內(nèi)核空間雖然很大,但總是有限的,而在這有限的空間中,其最后一個(gè)page是專門保留的,也就是說一般人不可能用到內(nèi)核空間最后一個(gè)page的指針。換句話說,你在寫設(shè)備驅(qū)動(dòng)程序的過程中,涉及到的任何一個(gè)指針,必然有三種情況:

  1. 有效指針;
  2. NULL,空指針;
  3. 錯(cuò)誤指針,或者說無效指針。

而所謂的錯(cuò)誤指針就是指其已經(jīng)到達(dá)了最后一個(gè)page,即內(nèi)核用最后一頁捕捉錯(cuò)誤。比如對于32bit的系統(tǒng)來說,內(nèi)核空間最高地址0xffffffff,那么最后一個(gè)page就是指的0xfffff000~0xffffffff(假設(shè)4k一個(gè)page),這段地址是被保留的。內(nèi)核空間為什么留出最后一個(gè)page?我們知道一個(gè)page可能是4k,也可能是更多,比如8k,但至少它也是4k,所以留出一個(gè)page出來就可以讓我們把內(nèi)核空間的指針來記錄錯(cuò)誤了。內(nèi)核返回的指針一般是指向頁面的邊界(4k邊界),即ptr & 0xfff == 0。如果你發(fā)現(xiàn)你的一個(gè)指針指向這個(gè)范圍中的某個(gè)地址,那么你的代碼肯定出錯(cuò)了。IS_ERR()就是判斷指針是否有錯(cuò),如果指針并不是指向最后一個(gè)page,那么沒有問題;如果指針指向了最后一個(gè)page,那么說明實(shí)際上這不是一個(gè)有效的指針,這個(gè)指針里保存的實(shí)際上是一種錯(cuò)誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯(cuò)誤,然后如果是,那么就調(diào)用PTR_ERR()來返回這個(gè)錯(cuò)誤代碼。因此,判斷一個(gè)指針是不是有效的,可用如下的方式:

#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)

(unsigned long)-1000L 應(yīng)該為 (unsigned long)-0x1000L!(因?yàn)?-0x1000 才是 0xFFFFF000),這應(yīng)該是內(nèi)核的一個(gè)bug吧!在2.6.30.4的內(nèi)核中是這樣定義的:

[plain]view plaincopy
print?
  1. #defineMAX_ERRNO4095
  2. #defineIS_ERR_VALUE(x)unlikely((x)>=(unsignedlong)-MAX_ERRNO)

即判斷是不是在(0xfffff000,0xffffffff)之間,因此,可以用IS_ERR()來判斷內(nèi)核函數(shù)的返回值是不是一個(gè)有效的指針。注意這里用unlikely()的用意!

至于PTR_ERR(), ERR_PTR(),只是強(qiáng)制轉(zhuǎn)換以下而已?,F(xiàn)在應(yīng)該知道為什么我寫返回錯(cuò)誤碼的時(shí)候也加個(gè)負(fù)號如 -ENOSYS這樣子了。而PTR_ERR()只是返回錯(cuò)誤代碼,也就是提供一個(gè)信息給調(diào)用者,如果你只需要知道是否出錯(cuò),而不在乎因?yàn)槭裁炊鲥e(cuò),那你當(dāng)然不用調(diào)用PTR_ERR()了。

而我們的錯(cuò)誤碼的值在內(nèi)存中定義都是這樣的(asm-generic/errno-base.h):

[plain]view plaincopy
print?
  1. ......
  2. #defineEPERM1/*Operationnotpermitted*/
  3. #defineENOENT2/*Nosuchfileordirectory*/
  4. #defineESRCH3/*Nosuchprocess*/
  5. #defineEINTR4/*Interruptedsystemcall*/
  6. #defineEIO5/*I/Oerror*/
  7. #defineENXIO6/*Nosuchdeviceoraddress*/
  8. #defineE2BIG7/*Argumentlisttoolong*/
  9. #defineENOEXEC8/*Execformaterror*/
  10. #defineEBADF9/*Badfilenumber*/
  11. #defineECHILD10/*Nochildprocesses*/
  12. #defineEAGAIN11/*Tryagain*/
  13. #defineENOMEM12/*Outofmemory*/
  14. #defineEACCES13/*Permissiondenied*/
  15. #defineEFAULT14/*Badaddress*/
  16. #defineENOTBLK15/*Blockdevicerequired*/
  17. #defineEBUSY16/*Deviceorresourcebusy*/
  18. #defineEEXIST17/*Fileexists*/
  19. #defineEXDEV18/*Cross-devicelink*/
  20. #defineENODEV19/*Nosuchdevice*/
  21. #defineENOTDIR20/*Notadirectory*/
  22. #defineEISDIR21/*Isadirectory*/
  23. #defineEINVAL22/*Invalidargument*/
  24. #defineENFILE23/*Filetableoverflow*/
  25. #defineEMFILE24/*Toomanyopenfiles*/
  26. #defineENOTTY25/*Notatypewriter*/
  27. #defineETXTBSY26/*Textfilebusy*/
  28. #defineEFBIG27/*Filetoolarge*/
  29. #defineENOSPC28/*Nospaceleftondevice*/
  30. #defineESPIPE29/*Illegalseek*/
  31. #defineEROFS30/*Read-onlyfilesystem*/
  32. #defineEMLINK31/*Toomanylinks*/
  33. #defineEPIPE32/*Brokenpipe*/
  34. #defineEDOM33/*Mathargumentoutofdomainoffunc*/
  35. #defineERANGE34/*Mathresultnotrepresentable*/
  36. ........
如果指針指向了最后一個(gè)page,那么說明實(shí)際上這不是一個(gè)有效的指針。這個(gè)指針里保存的實(shí)際上是一種錯(cuò)誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯(cuò)誤,然后如果是,那么就調(diào)用PTR_ERR()來返回這個(gè)錯(cuò)誤代碼。


關(guān)鍵詞: linux內(nèi)核IS_ER

評論


技術(shù)專區(qū)

關(guān)閉