IS_ERR()函数分析(转)

上一篇 / 下一篇  2012-08-07 21:06:43 / 个人分类:linux内核

IS_ERR是一个宏,用来判断指针的.当你创建了一个进程,你当然想知道这个进程创建成功了没有.以前我们注意到每次申请内存的时候都会做一次判断,你说创建进程是不是也要申请内存?不申请内存谁来记录struct task_struct?很显然,要判断.以前我们判断的是指针是否为空,但是Linux内核不是上海滩,越老越经典.以后接触代码多了你会发现,我们以前在usb storage申请内存的时候用的都是kmalloc(),但是其实Linux内核中有很多种内存申请的方式,而这些方式所返回的内存地址也是不一样的,所以并不是每一次我们都只要判断指针是否为空就可以了.事实上,每一次调用kthread_run()之后,我们都会用一个IS_ERR()来判断指针是否有效.IS_ERR()1就表示指针有错,或者准确一点说叫做指针无效.什么叫指针无效?如果你和我一样,觉得生活很无聊,那么本节最后一段会专门给你个解释,其他人就不用去管IS_ERR(),让我们继续往下看,只需要记得,如果你不希望发生缺页异常这样的错误的话,那么请记住,每次调用完kthread_run()之后要用IS_ERR()来检测一下返回的指针.如果IS_ERR()返回值是0,那么说明没有问题,于是return 0,也就是说usb_hub_init()就这么结束了.反之,就会执行usb_deregister(),因为内核线程没有成功创建,hub就没法驱动起来了.于是就没有必要瞎耽误工夫了,该干嘛干嘛去.最后函数在2870,返回-1.回到usb_init()中我们会知道,接下来usb_hub_cleanup()就会被调用.usb_hub_cleanup()同样定义于drivers/usb/core/hub.c,

   2873 void usb_hub_cleanup(void)

   2874 {

   2875         kthread_stop(khubd_task);

   2876

   2877         /*

   2878          * Hub resources are freed for us by usb_deregister. It calls

   2879          * usb_driver_purge on every device which in turn calls that

   2880          * devices disconnect function if it is using this driver.

   2881          * The hub_disconnect function takes care of releasing the

   2882          * individual hub resources. -greg

   2883          */

   2884         usb_deregister(&hub_driver);

   2885 } /* usb_hub_cleanup() */

这个函数我想没有任何必要解释了吧.kthread_stop()和刚才的kthread_run()对应,usb_deregister()usb_register()对应.

总之,如果创建子进程出了问题,那么一切都免谈.啥也别玩了.歇菜了.

反之,如果成功了,那么kthread_run()的三个参数就是我们要关注的了,第一个hub_thread(),子进程将从这里开始执行.第二个是hub_thread()的参数,传递的是NULL,第三个参数就是精灵进程的名字,ps –el看一下,比如像偶的这样子:

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ps -el | grep khubd

1 S     0  1963    27  0  70  -5 -     0 hub_th ?        00:00:00 khubd

你就会发现有这么一个精灵进程运行着.所以,下一步,让我们进入hub_thread()来看看这个子进程吧,很显然,关于父进程,我们没什么好看的了.

======================华丽的分割线=====================

人的无聊,有时候很难用语言表达.以下关于IS_ERR的文字仅献给无聊的你.如果你对内存管理没有任何兴趣,就不用往下看了,跳到下一节吧.要想明白IS_ERR(),首先你得知道有一种空间叫做内核空间,不清楚也不要紧,我也不是很清楚,曾经,在复旦,操作系统这门课的时候,我一度以为我已经成为天使了,因为我天天上课都在听天书.后来,确切地说是去年,我去微软全球技术中心(GSTC)面试的时候,那个manager就要我解释这个名词,要我谈一谈对内核空间和用户空间的理解,其实我也挺纳闷的,我只不过是希望能成为微软的一名技术支持工程师,居然还要懂内核,你说这是什么世道?中学时候,老师不是跟我说只要学好数理化,走遍天下都不怕吗?算了,不去想这些伤心往事了.结合IS_ERR()的代码来看,来自include/linux/err.h:

      8 /*

      9  * Kernel pointers have redundant information, so we can use a

     10  * scheme where we can return either an error code or a dentry

     11  * pointer with the same return value.

     12  *

     13  * This should be a per-architecture thing, to allow different

     14  * error and pointer decisions.

     15  */

     16 #define MAX_ERRNO       4095

     17

     18 #ifndef __ASSEMBLY__

     19

     20 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

     21

     22 static inline void *ERR_PTR(long error)

     23 {

     24         return (void *) error;

     25 }

     26

     27 static inline long PTR_ERR(const void *ptr)

     28 {

     29         return (long) ptr;

     30 }

     31

     32 static inline long IS_ERR(const void *ptr)

     33 {

     34         return IS_ERR_VALUE((unsigned long)ptr);

     35 }

     36

     37 #endif

关于内核空间,我只想说,所有的驱动程序都是运行在内核空间,内核空间虽然很大,但总是有限的.要知道即便是我们这个幅员辽阔的伟大祖国其空间也是有限的,也只有960万平方公里,所以内核空间当然也是一个有限的空间,而在这有限的空间中,其最后一个page是专门保留的,也就是说一般人不可能用到内核空间最后一个page的指针.换句话说,你在写设备驱动程序的过程中,涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,一般人不得越雷池半步,如果你发现你的一个指针指向这个范围中的某个地址,那么恭喜你,你的代码肯定出错了.

那么你是不是很好奇,好端端的内核空间干嘛要留出最后一个page?这不是缺心眼儿吗?明明自己有1000块钱,非得对自己说只能用900.实在不好意思,你说错了,这里不仅不是浪费一个page,反而是充分利用资源,把一个东西当两个东西来用.

看见16行那个MAX_ERRNO了吗?一个宏,定义为4095,MAX_ERRNO就是最大错误号,Linux内核中,出错有多种可能,因为有许许多多种错误,就像一个人进监狱,可能是像迟志强那样,在事业如日中天的时候强奸女孩,可能是像张君大哥那样,因为抢劫长沙友谊商城后又抢农业银行,亦或者是马加爵大侠那样,受同学的气,最终让铁锤来说话.关于Linux内核中的错误,我们看一下include/asm-generic/errno-base.h文件:

#define EPERM            1      /* Operation not permitted */

#define ENOENT           2      /* No such file or directory */

#define ESRCH            3      /* No such process */

#define EINTR            4      /* Interrupted system call */

#define EIO              5      /* I/O error */

#define ENXIO            6      /* No such device or address */

#define E2BIG            7      /* Argument list too long */

#define ENOEXEC          8      /* Exec format error */

#define EBADF            9      /* Bad file number */

#define ECHILD          10      /* No child processes */

#define EAGAIN          11      /* Try again */

#define ENOMEM          12      /* Out of memory */

#define EACCES          13      /* Permission denied */

#define EFAULT          14      /* Bad address */

#define ENOTBLK         15      /* Block device required */

#define EBUSY           16      /* Device or resource busy */

#define EEXIST          17      /* File exists */

#define EXDEV           18      /* Cross-device link */

#define ENODEV          19      /* No such device */

#define ENOTDIR         20      /* Not a directory */

#define EISDIR          21      /* Is a directory */

#define EINVAL          22      /* Invalid argument */

#define ENFILE          23      /* File table overflow */

#define EMFILE          24      /* Too many open files */

#define ENOTTY          25      /* Not a typewriter */

#define ETXTBSY         26      /* Text file busy */

#define EFBIG           27      /* File too large */

#define ENOSPC          28      /* No space left on device */

#define ESPIPE          29      /* Illegal seek */

#define EROFS           30      /* Read-only file system */

#define EMLINK          31      /* Too many links */

#define EPIPE           32      /* Broken pipe */

#define EDOM            33      /* Math argument out of domain of func */

#define ERANGE          34      /* Math result not representable */

最常见的几个是-EBUSY,-EINVAL,-ENODEV,-EPIPE,-EAGAIN,-ENOMEM,我相信不用说你写过代码调试过代码,只要你使用过Linux就有可能见过这几个错误,因为它们确实经常出现.这些是每个体系结构里都有的,另外各个体系结构也都定义了自己的一些错误代码.这些东西当然也都是宏,实际上对应的是一些数字,这个数字就叫做错误号.而对于Linux内核来说,不管任何体系结构,最多最多,错误号不会超过4095.4095又正好是比4k1,40961.而我们知道一个page可能是4k,也可能是更多,比如8k,但至少它也是4k,所以留出一个page出来就可以让我们把内核空间的指针来记录错误了.什么意思呢?比如我们这里的IS_ERR(),它就是判断kthread_run()返回的指针是否有错,如果指针并不是指向最后一个page,那么没有问题,申请成功了,如果指针指向了最后一个page,那么说明实际上这不是一个有效的指针,这个指针里保存的实际上是一种错误代码.而通常很常用的方法就是先用IS_ERR()来判断是否是错误,然后如果是,那么就调用PTR_ERR()来返回这个错误代码.只不过咱们这里,没有调用PTR_ERR()而已,因为起决定作用的还是IS_ERR(),PTR_ERR()只是返回错误代码,也就是提供一个信息给调用者,如果你只需要知道是否出错,而不在乎因为什么而出错,那你当然不用调用PTR_ERR(),毕竟,男人,简单就好.当然,这里如果出错了的话,最终usb_deregister()会被调用,并且usb_hub_init()会返回-1.


TAG:

 

评分:0

我来说两句

日历

« 2024-05-07  
   1234
567891011
12131415161718
19202122232425
262728293031 

数据统计

  • 访问量: 54912
  • 日志数: 40
  • 建立时间: 2012-07-17
  • 更新时间: 2012-10-01

RSS订阅

Open Toolbar