IS_ERR()说明

 感谢fudan_abc的分享,本篇文章选自他的《Linux那些事儿之我是Hub(3)一样的精灵不一样的API》     

        人的无聊,有时候很难用语言表达.以下关于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又正好是比4k小1,即4096减1.而我们知道一个page可能是4k,也可能是更多,比如8k,但至少它也是4k,所以留出一个page出来就可以让我们把内核空间的指针来记录错误了.什么意思呢?比如我们这里的IS_ERR(),它就是判断kthread_run()返回的指针是否有错,如果指针并不是指向最后一个page,那么没有问题,申请成功了,如果指针指向了最后一个page,那么说明实际上这不是一个有效的指针,这个指针里保存的实际上是一种错误代码.而通常很常用的方法就是先用IS_ERR()来判断是否是错误,然后如果是,那么就调用PTR_ERR()来返回这个错误代码.只不过咱们这里,没有调用PTR_ERR()而已,因为起决定作用的还是IS_ERR(),而

PTR_ERR()只是返回错误代码,也就是提供一个信息给调用者,如果你只需要知道是否出错,而不在乎因为什么而出错,那你当然不用调用PTR_ERR()了,毕竟,男人,简单就好


以上文字转载自:点击打开链接


以下文字转载自: 点击打开链接


 下面是本人对于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,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了最 后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的 0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,如果超过这个地址,则肯定是错误的。

<think>首先,用户在询问IS_ERR函数的返回值。根据提供的引用,IS_ERR是一个宏或内联函数,用于检查一个指针是否表示一个错误。 关键点从引用中提取: - IS_ERR() 用于检测指针是否编码了错误码。 - 引用[3] 解释说,合法的指针范围之外,内核保留了一段地址空间来表示错误码(0xfffffffffffff000 ~ 0xffffffffffffffff 在64位系统上)。 - 如果指针落在这个范围内,IS_ERR() 返回 true(非零值),表示错误。 - 否则,它返回 false(零),表示指针有效。 现在,IS_ERR本身的返回值: - 它是一个宏或函数,返回一个boolean值。具体而言,它检查指针的值是否在错误码范围内。 - 它返回一个整数值:如果错误则为非零(true),如果指针有效则为零(false)。 用户的问题是:"IS_ERR函数的返回值",意思是"IS_ERR函数的返回值"。我需要清晰地解释它返回什么。 根据引用: - 引用[4] 提到:"IS_ERR()只是用来检测指针是否返回错误的指针。" - 它返回true或false。 在代码中,它通常这样使用: ```c if (IS_ERR(ptr)) { int error = PTR_ERR(ptr); // 处理错误 } ``` 所以,IS_ERR() 返回一个布尔值:true 表示错误,false 表示没有错误。 具体返回值类型: - 在Linux内核中,它是通过宏实现的,可能返回一个int值(0或1)或bool类型。 为了回答: - 我应该说明 IS_ERR() 返回一个整数或其他类型,但本质上是布尔值的含义。 - 它返回非零值(true)如果指针是错误指针,否则返回零(false)。 组织我的回答: 1. 简要解释 IS_ERR 的作用。 2. 描述其返回值。 3. 提供示例(如果可能)。 既然用户参考了具体引用,我应该在回答中自然地添加引用标识。 最后,生成相关问题,这是系统指令的一部分。 确保语言为中文。 回答结构: - 清晰、逐步。 - 真实可靠,基于引用。 </think>### IS_ERR函数的返回值详解 在Linux内核开发中,`IS_ERR()` 是一个关键宏(或内联函数),用于检测内核函数返回的指针是否编码了错误信息。它的返回值是一个布尔值(boolean),具体解释如下: 1. **返回值类型和含义**: - `IS_ERR()` 返回一个 **整数(int)** 值,本质上是布尔逻辑: - **非零值(true)**:表示指针是错误指针(即指针编码了错误码),例如返回 `1` 或非零值。这意味着内核函数调用失败,需要通过 `PTR_ERR()` 提取具体的错误码(如 `-ENOMEM` 或 `-EINVAL`)[^3][^4]。 - **零值(false)**:表示指针是有效地址,没有错误。使用者可以直接操作该指针[^1][^3]。 2. **原理背景**: - 在64位系统中,内核保留了高地址区间(`0xfffffffffffff000 ~ 0xffffffffffffffff`)用于存储错误码。`IS_ERR()` 通过检查指针值是否落在这个范围内来判断: - 如果指针值在此范围内,`IS_ERR()` 返回 true。 - 否则,返回 false[^3][^4]。 3. **代码示例**: 以下是一个典型用法片段(基于引用[2]),展示了 `IS_ERR()` 的返回值如何用于错误处理: ```c struct class *myclass = class_create(THIS_MODULE, "myclass"); if (IS_ERR(myclass)) { // IS_ERR返回非零值时进入此分支 int ret = PTR_ERR(myclass); // 提取错误码 goto fail; // 处理错误 } // 其他操作:如果IS_ERR返回0,指针有效 ``` 在这个例子中,`IS_ERR(myclass)` 的返回值直接用于 `if` 条件判断。如果返回非零值,表示 `class_create` 失败;返回零值,则可以继续使用 `myclass` 指针[^2][^3]。 4. **关键注意事项**: - `IS_ERR()` 本身 **不返回错误码**,只返回布尔状态。要获取具体的错误号(如 `-EIO`),需要配合 `PTR_ERR()` 使用[^3][^4]。 - 错误码范围由 `MAX_ERRNO`(通常为4095)定义,确保了指针值和错误码的可逆转换(通过 `ERR_PTR()` 和 `PTR_ERR()`)[^3][^4]。 总之,`IS_ERR()` 的返回值是一个简单的布尔标志:非零表示错误,零表示成功。这使得开发者能高效检测内核指针的合法性[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值