微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > linux内核中的IS_ERR

linux内核中的IS_ERR

时间:11-22 来源:互联网 点击:
在看内核源码的时候,经常会遇到IS_ERR,比如在linux/arch/arm/kernel/sys_arm.c中

[plain]view plaincopyprint?

  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 plaincopyprint?

  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 plaincopyprint?

  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 plaincopyprint?

  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

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top