IS_ERR

像struct class *cls = class_create();这种语句,其中返回的指针值并不行kmalloc一样这么简单,只判断是否为NULL就可以了,内核是返回其错误值。那么我怎么来判断它呢,总不能用if()来将每个错误例出来吧,这里我们的IS_ERR()宏就发挥作用了。先看源代码,再讲原理,看看内核中的巧妙设计思路。

/*    include/linux/err.h    */
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)


内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来。
所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即

ptr & 0xfff == 0

这样ptr的值不可能落在(0xfffff000,0xffffffff)之间,而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsigned long,正好在(0xfffff000,0xffffffff)之间。因此可以用

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

也就等效于(x) >= (unsigned long)-MAX_ERRNO
其中MAX_ERRNO 为4095

来判断内核函数的返回值是一个有效的指针,还是一个出错代码。

涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了 最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的 0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,如果超过这个地址,则肯定是错误的。

而我们的错误码的值在内存中定义都是这样的(include/linux/errno.h):
......
#define    ENOLCK        77    /* No record locks available */
#define    ENOSYS        78    /* Function not implemented */
#define    ENOMSG        80    /* No message of desired type */
#define    EIDRM        81    /* Identifier removed */
#define    ENOSR        82    /* Out of streams resources */
#define    ETIME        83    /* Timer expired */
#define    EBADMSG        84    /* Not a data message */
#define    EPROTO        85    /* Protocol error */
#define    ENODATA        86    /* No data available */
#define    ENOSTR        87    /* Device not a stream */.
........

现在应该知道为什么我写返回错误码的时候也加个负号如 -ENOSYS这样子了。

至于PTR_ERR(), ERR_PTR(),只是强制转换以下而已,源代码如下(include/linux/err.h) :
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;
}

static inline long __must_check PTR_ERR(const void *ptr)
{
return (long) ptr;
}



所以像上面的cls例子可以这样写:
struct class *cls = class_create(....);
if(IS_ERR(cls))
{
ret =PTR_ERR(cls);
return ret;
}
<think>好的,用户之前让我帮忙写一篇关于Linux系统调试中IS_ERR函数使用的优快云博文,现在他们又直接询问IS_ERR函数的使用。我需要先回顾之前的对话,确保这次回答与之前的内容一致,同时更简洁明了。 首先,用户可能已经阅读了之前的博文,但需要更简明的介绍,或者他们希望快速了解关键点,而不是详细的博文结构。所以这次回答需要重点突出IS_ERR的作用、使用场景、常见错误和示例,同时保持结构清晰。 接下来,我要检查用户的需求是否有变化。之前的请求是写一篇博文,现在则是介绍函数的使用,可能用户需要更直接的说明,适合快速参考。需要确认用户是否希望深入技术细节还是更偏向实用指南。 用户可能是在开发或调试Linux内核模块时遇到了问题,需要正确使用IS_ERR来处理错误。他们可能遇到了误用IS_ERR的情况,比如对返回NULL的函数使用IS_ERR,导致错误处理失效。因此,回答中需要强调哪些函数返回错误指针,哪些返回NULL,以及如何正确判断。 同时,用户可能需要示例代码来理解如何在实际代码中应用IS_ERR、PTR_ERRERR_PTR。需要确保示例简洁,并标注常见错误,帮助用户避免常见陷阱。 还要考虑用户的知识水平,他们可能已经具备一定的内核开发经验,但需要明确函数的使用场景和原理。需要解释错误码编码的原理,比如内核如何将错误码转换为指针值,以及不同体系结构下的差异,但可能不需要太深入的架构细节,除非用户特别要求。 最后,总结部分需要以表格或列表形式呈现关键点,方便用户快速查阅。同时提醒调试技巧,如打印错误码和使用dump_stack(),这些实际调试方法对用户解决问题可能有直接帮助。 需要确保回答准确无误,符合内核实际实现,比如确认IS_ERR检查的指针范围,以及相关宏的定义。避免技术错误,比如混淆返回错误指针和NULL的函数,这点在之前的博文中已经提到,需要再次强调。 可能用户的问题背后是想解决某个具体的bug,但未明确说明,因此回答需要覆盖常见错误场景,帮助他们自查代码中的问题。同时,保持语言简洁,结构清晰,便于用户快速获取所需信息。</think>### IS_ERR函数使用详解 #### 1. 核心作用 `IS_ERR`是Linux内核中用于**检测指针是否包含错误码**的宏。它解决了内核API返回指针时无法直接传递错误码的问题。 #### 2. 典型使用场景 ```c // 资源获取类函数的返回值检查 struct clk *clk = clk_get(dev, "core_clk"); if (IS_ERR(clk)) { // 错误处理 int err = PTR_ERR(clk); // 提取原始错误码 return ERR_PTR(err); // 向上层传递错误 } ``` #### 3. 必须搭配的宏 | 宏 | 作用 | 示例 | |---------------|-------------------------------|-------------------------------| | `IS_ERR()` | 判断指针是否为错误码 | `if (IS_ERR(ptr))` | | `PTR_ERR()` | 从指针提取原始错误码 | `int err = PTR_ERR(ptr);` | | `ERR_PTR()` | 将错误码转换为错误指针 | `return ERR_PTR(-ENOMEM);` | #### 4. 关键注意事项 1. **适用函数范围** - 必须用于返回错误指针的API:`ioremap()`, `clk_get()`, `regmap_init()`等 - 禁止用于返回NULL的API:`kmalloc()`, `vmalloc()`, `kzalloc()`等 2. **常见错误类型** ```c // 错误用法示例 char *buf = kmalloc(1024, GFP_KERNEL); if (IS_ERR(buf)) { // 错误!kmalloc失败返回NULL,不是错误指针 // 永远不会进入此分支 } ``` 3. **调试技巧** ```c if (IS_ERR(ptr)) { pr_err("Operation failed: %ld (0x%lx)\n", PTR_ERR(ptr), (unsigned long)ptr); // 打印十进制错误码和指针值 dump_stack(); // 显示调用栈 } ``` #### 5. 底层实现原理 通过判断指针值是否落在内核空间的错误码区间(通常为虚拟地址空间的最后4KB): ```c // x86架构实现示例 #define MAX_ERRNO 4095 #define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) static inline bool IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } ``` #### 6. 错误码对照表 | 错误码 | 值 | 含义 | |------------------|-------|-----------------------| | `-EPERM` | 1 | 操作不允许 | | `-ENOENT` | 2 | 无此文件/目录 | | `-EIO` | 5 | I/O错误 | | `-ENOMEM` | 12 | 内存不足 | | `-EFAULT` | 14 | 错误地址访问 | #### 7. 正确使用流程 1. 调用返回指针的内核API 2. 使用`IS_ERR()`检查返回值 3. 若检测到错误,用`PTR_ERR()`提取错误码 4. 通过`ERR_PTR()`或`return err`传递错误 #### 8. 典型错误模式 ```c // 错误案例:混淆错误指针和NULL void __iomem *addr = ioremap(phys_addr, size); if (!addr) { // 错误!ioremap失败返回ERR_PTR(-ENXIO) return -ENOMEM; // 错误处理不生效 } // 正确用法 if (IS_ERR(addr)) { return PTR_ERR(addr); } ``` 掌握`IS_ERR`系列函数的使用可以有效定位以下问题: - 设备资源申请失败 - 内存映射错误 - 时钟/中断等核心资源获取失败 - 硬件寄存器访问异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值