歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

linux內核中的IS_ERR

linux內核中的IS_ERR   linux內核中的IS_ERR()、PTR_ERR()和ERR_PTR() 在看內核源碼的時候,經常會遇到IS_ERR,比如在 linux/arch/arm/kernel/sys_arm.c中 [plain]   www.2cto.com   asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv,                 char __user * __user *envp, struct pt_regs *regs)   {       int error;       char * filename;             filename = getname(filenamei);       error = PTR_ERR(filename);       if (IS_ERR(filename))           goto out;       error = do_execve(filename, argv, envp, regs);       putname(filename);   out:       return error;   }   IS_ERR宏定義在include/linux/err.h,如下所示:  www.2cto.com   [plain]  #ifndef _LINUX_ERR_H   #define _LINUX_ERR_H      #include <linux/compiler.h>      #include <asm/errno.h>      /*    * Kernel pointers have redundant information, so we can use a    * scheme where we can return either an error code or a dentry    * pointer with the same return value.    *    * This should be a per-architecture thing, to allow different    * error and pointer decisions.    */   #define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)      static inline void *ERR_PTR(long error)   {       return (void *) error;   }      static inline long PTR_ERR(const void *ptr)   {       return (long) ptr;   }      static inline long IS_ERR(const void *ptr)   {       return IS_ERR_VALUE((unsigned long)ptr);   }      #endif /* _LINUX_ERR_H */   下面我們就來具體分析一下這段代碼,看看內核中的巧妙設計思路。 要想明白IS_ERR(),首先理解要內核空間。所有的驅動程序都是運行在內核空間,內核空間雖然很大,但總是有限的,而在這有限的空間中,其最後一個page是專門保留的,也就是說一般人不可能用到內核空間最後一個page的指針。換句話說,你在寫設備驅動程序的過程中,涉及到的任何一個指針,必然有三種情況: 有效指針; NULL,空指針; 錯誤指針,或者說無效指針。 而所謂的錯誤指針就是指其已經到達了最後一個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]  #define MAX_ERRNO   4095   #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)   即判斷是不是在(0xfffff000,0xffffffff)之間,因此,可以用IS_ERR()來判斷內核函數的返回值是不是一個有效的指針。注意這裡用unlikely()的用意! 至於PTR_ERR(), ERR_PTR(),只是強制轉換以下而已。現在應該知道為什麼我寫返回錯誤碼的時候也加個負號如 -ENOSYS這樣子了。而PTR_ERR()只是返回錯誤代碼,也就是提供一個信息給調用者,如果你只需要知道是否出錯,而不在乎因為什麼而出錯,那你當然不用調用PTR_ERR()了。 而我們的錯誤碼的值在內存中定義都是這樣的(asm-generic/errno-base.h): [plain]  ......   #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 */   ........   如果指針指向了最後一個page,那麼說明實際上這不是一個有效的指針。這個指針裡保存的實際上是一種錯誤代碼。而通常很常用的方法就是先用IS_ERR()來判斷是否是錯誤,然後如果是,那麼就調用PTR_ERR()來返回這個錯誤代碼。
Copyright © Linux教程網 All Rights Reserved