新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > linux內核中的IS_ERR

        linux內核中的IS_ERR

        作者: 時間:2016-11-22 來源:網絡 收藏
        在看內核源碼的時候,經常會遇到IS_ERR,比如在linux/arch/arm/kernel/sys_arm.c中

        本文引用地址:http://www.104case.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*/
        下面我們就來具體分析一下這段代碼,看看內核中的巧妙設計思路。

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

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

        而所謂的錯誤指針就是指其已經到達了最后一個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的內核中是這樣定義的:

        [plain]view plaincopy
        print?
        1. #defineMAX_ERRNO4095
        2. #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):

        [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. ........
        如果指針指向了最后一個page,那么說明實際上這不是一個有效的指針。這個指針里保存的實際上是一種錯誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯誤,然后如果是,那么就調用PTR_ERR()來返回這個錯誤代碼。


        關鍵詞: linux內核IS_ER

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 天全县| 雅安市| 涞源县| 锡林郭勒盟| 瑞金市| 穆棱市| 于田县| 杭州市| 桐梓县| 太谷县| 巩留县| 都兰县| 内黄县| 崇文区| 奉化市| 安吉县| 万载县| 余江县| 合水县| 托里县| 平泉县| 平原县| 陵川县| 甘德县| 广州市| 禄劝| 牟定县| 得荣县| 张北县| 房产| 赤壁市| 池州市| 乌鲁木齐县| 星座| 喜德县| 宣化县| 荥阳市| 昂仁县| 巴南区| 普安县| 防城港市|