linux内核中的IS_ERR()、PTR_ERR()和ERR_PTR()

linux内核中的IS_ERR()、PTR_ERR()和ERR_PTR()

 

在看内核源码的时候,经常会遇到IS_ERR,比如在 linux/arch/arm/kernel/sys_arm.c中

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,如下所示:

#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吧!

这实际上并不是一个bug,一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsigned long,正好在(0xfffff000,0xffffffff)之间,因此可以用(unsigned long)-1000L来判断内核函数的返回值是一个有效的指针,还是一个出错代码。  而4095则是包含了整个(0xfffff000,0xffffffff)范围。

 

在2.6.30.4的内核中是这样定义的:

#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):

......
#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()来返回这个错误代码。

---------------------

作者:ce123

来源:CSDN

原文:https://blog.csdn.net/ce123_zhouwei/article/details/8450618

版权声明:本文为博主原创文章,转载请附上博文链接!

展开阅读全文

没有更多推荐了,返回首页