对内核源码中IS_ERR的理解

来源:互联网 发布:ps淘宝主图怎么修改 编辑:程序博客网 时间:2024/06/02 04:19

转载自http://blog.csdn.net/moonvs2010/article/details/7790422  作者:moonvs2010

 

 

今天在看内核源码时,看到一个判断指针是否是错误指针或无效指针的函数IS_ERR(2.6.11内核,include/linux/err.h中),其源码如下:

[cpp] view plaincopyprint?
  1. static inlinelong IS_ERR(constvoid *ptr)
  2. {
  3. return unlikely((unsignedlong)ptr > (unsigned long)-1000L);
  4. }

这个函数非常简单,只是将指针的值和-1000L进行比较,如果ptr的值大于(unsigned long)-1000L,则表示ptr是一个无效指针或错误指针。很奇怪,不知道为什么要和-1000L进行比较,所以进行一番搜索和学习,现将自己的理解整理一下,记录下来,跟大家分享一下。

Linux中的指针分为三类:有效指针、空指针(NULL, 通常为0)和无效指针(错误指针)。这里只关心无效指针,什么样的指针式无效指针?根据IS_ERR的判断,如果指针的值大于(unsigned long)-1000L,则这个指针是无效指针。将-1000L通过十六进制输出其值为0xfffffc18,这个值刚好在0xfffff000到0xffffffff之间,而这个区间正好是32位下4G内存空间的最后一页。根据查阅的资料,之前版本的内核不是和-1000L比较,而是和-4095比较,也就是说指针的值只要是在最后一页就认为是无效指针。所以Linux是将最后一页作为来判定是否是错误指针来使用的,当然现在的范围只是最后一页的一部分0xfffffc18到0xffffffff之间,根据include/linux/err.h文件的注释,Linux内核中返回的无效指针中包含更多的信息,包含错误码或者一个目录项指针。

先看下面的一个函数,

[cpp] view plaincopyprint?
  1. struct net_device * __init el1_probe(int unit)
  2. {
  3. struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
  4. static unsigned ports[] = { 0x280, 0x300, 0};
  5. unsigned *port;
  6. int err = 0;
  7. <SPAN style="COLOR: #ff0000"><STRONG>if (!dev)
  8. return ERR_PTR(-ENOMEM);</STRONG>
  9. </SPAN>
  10. if (unit >= 0) {
  11. sprintf(dev->name, "eth%d", unit);
  12. netdev_boot_setup_check(dev);
  13. io = dev->base_addr;
  14. irq = dev->irq;
  15. mem_start = dev->mem_start & 7;
  16. }
  17. SET_MODULE_OWNER(dev);
  18. if (io > 0x1ff) {/* Check a single specified location. */
  19. err = el1_probe1(dev, io);
  20. } else if (io != 0) {
  21. err = -ENXIO; /* Don't probe at all. */
  22. } else {
  23. for (port = ports; *port && el1_probe1(dev, *port); port++)
  24. ;
  25. if (!*port)
  26. err = -ENODEV;
  27. }
  28. if (err)
  29. goto out;
  30. err = register_netdev(dev);
  31. if (err)
  32. goto out1;
  33. return dev;
  34. out1:
  35. release_region(dev->base_addr, EL1_IO_EXTENT);
  36. out:
  37. free_netdev(dev);
  38. return ERR_PTR(err);
  39. }

主要看红色的部分,当dev分配失败时,返回的是ERR_PTR(-ENOMEM),相当于是(void *)(-ENOMEM),而这个返回的指针的值转换成16进制后也落在这个值刚好在0xfffffc18到0xffffffff之间。所以当Linux判断一个指针是一个无效指针时,仅仅通过这个无效的指针就可以拿到错误码,知道错误原因,而不需要用另一个变量来返回错误原因。其实说了那么一大堆废话,就是想引出这个结论。平时在写程序时,如果需要某个函数返回一个指针,发生错误时返回NULL,但仅仅知道一个NULL,是不能判断究竟发生了什么错误,还需要另外一个位置来存储错误,从这点就可以看出内核设计的一些细节非常值得我们学习和借鉴。

如果目录项的指针位于0xfffffc18到0xffffffff之间,也表示这个目录项指针是一个无效指针,因为最后一页是不使用的,用来标示错误。至于内核为什么把范围的起始地址从0xfffff000该为0xfffffc18,现在还不知道,知道的同学可以留个言,共同学习。

另外本人的表达能力不好,如果有什么疑惑也可以留言,交流一下。