Linux中IS_ERR()函數的理解

來源:互聯網
上載者:User

轉自http://jimmy-lee.blog.hexun.com/6075934_d.html

和http://blog.chinaunix.net/u3/97568/showart_1978276.html兩位的部落格。謝兩位分享。

在Linux源碼中的fs部分,經常會碰到這樣的函數(位於kernel/include/linux/fs.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.
 */
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 (unsigned long)ptr > (unsigned long)-1000L;
}

    下面是本人對於IS_ERR函數的理解,不完全是正確的,如果理解有錯誤,請告之我.

    在IS_ERR()函數中(unsigned long)-1000L實際上表示的是0xFFFFF000(因為負數在電腦中是原碼的補碼加一),在linux中虛擬記憶體空間的分配,0~3G是給使用者空間的,而3G~4G是給linux核心的,而0xFFFFF000就位於linux核心的虛擬記憶體空間範圍內,從0xFFFFF000到4G間的大小隻有4KB,這實際上也就是一個PAGE_SIZE的大小,這時如果一個指標位於這塊4KB的地區,則這個指標也就不可能是一個頁面的首地址了,因為這已經不足以分配一個頁面了。

    這核心虛擬空間的top 4KB一般是不作為分配空間來使用的。(我沒有找到確切的證據是這樣的,只是根據後面的分析覺得這塊空間保留,其位址範圍用來進行錯誤判斷).

    如果傳遞給IS_ERR()函數的參數是一個頁面的首地址指標,那麼必然是一個錯誤指標。
    IS_ERR()也可以用來檢測一個錯誤碼,這就是與ERR_PTR()配合使用了,看下面一小段代碼:(kernel/fs/namespace.c/sys_mount())

asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
                                               unsigned long flags, void * data)
{
    int retval;
    ....
    char *dir_page;
    ....

    dir_page = getname(dir_name);
     retval = PTR_ERR(dir_page);
     if (IS_ERR(dir_page))
          goto out1;

    ....
}

    getname()返回有可能是一個分配的頁面的首地址,也有可能因為記憶體不足返回ERR_PTR(-ENOMEM);先看返回是頁面首地址的情況,接著通過PTR_ERR()將這個指標類型的地址轉化成為一個整型,再通過IS_ERR()來判斷是否是一個有效頁面首地址,這跟前面分析的一樣.
    再接下來看一下,如果返回的是錯誤碼的情況,ENOMEM在kernel/include/asm-*/error.h中定義的值是12,經過ERR_PTR(-ENOMEM)返回則成了指標類型,指向0xFFFFFFF4,就指標而言它是指向虛擬核心空間的top4KB空間,再通過IS_ERR()判斷返回的是false。

    在linux中我們看到錯誤碼ERRCODE的值從1~??,這個??不太可能大於4KB的,所以通過ERR_PTR(-ERRCODE),則映射到了虛擬核心空間的top4KB(0xFFFFF000~4G)去了,再通過IS_ERR()即可檢測出"is error"!

    綜上述,IS_ERR()可以檢測頁面首地址是否有效,也可以檢測出錯誤碼.

 

IS_ERR()有一些妙處。
核心中的函數常常返回指標,問題是如果出錯,也希望能夠通過返回的指標體現出來。
所幸的是,核心返回的指標一般是指向頁面的邊界(4K邊界),即

ptr & 0xfff == 0

這樣ptr的值不可能落在(0xfffff000,0xffffffff)之間,
而一般核心的出錯代碼也是一個小負數,在-1000到0之間,轉變成unsigned long,
正好在(0xfffff000,0xffffffff)之間。因此可以用

(unsigned long)ptr > (unsigned long)-1000L

來判斷核心功能的傳回值是一個有效指標,還是一個出錯代碼。

涉及到的任何一個指標,必然有三種情況,一種是有效指標,一種是NULL,null 指標,一種是錯誤指標,或者說無效指標.而所謂的錯誤指標就是指其已經到達了最後一個page.比如對於32bit的系統來說,核心空間最高地址0xffffffff,那麼最後一個page就是指的 0xfffff000~0xffffffff(假設4k一個page).這段地址是被保留的,如果超過這個地址,則肯定是錯誤的。
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等等,可以看到,這個值實際上是在-1000~0之間的。

對於一個返回指標的函數,我們通常返回NULL表示失敗,但是這不能指出那種失敗(記憶體不足?硬體錯誤還是網路不可達?)
所以返回的時候用ERR_PTR(-ENOME) 等就可以判斷,因為這個指標顯然不合法
參考 include/iinux/err.h

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.