linux内核err实现,Linux中IS_ERR()函数的理解
在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;
}*/
#define MAX_ERRNO??? 4095
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
下面是本人對于IS_ERR函數的理解,不完全是正確的,如果理解有錯誤,請告之我.
在IS_ERR()函數中 (unsigned long)-MAX_ERRNO實際上表示的是0xFFFFF001(因為負數在計算機中是原碼的補碼),在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,空指針,一種是錯誤指針,或者說無效指針.而所謂的錯誤指針就是指其已經到達了最
后一個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
總結
以上是生活随笔為你收集整理的linux内核err实现,Linux中IS_ERR()函数的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ascii码01100001_ASCII
- 下一篇: linux系统安装python包_Lin