当前位置: 代码网 > 服务器>服务器>Linux > Linux深入解析IS_ERR函数的使用方式

Linux深入解析IS_ERR函数的使用方式

2025年07月11日 Linux 我要评论
一、为什么需要is_err?在linux内核开发中,内核空间的函数(如内存分配、设备驱动接口)无法像用户空间那样直接返回-enomem或-einval等错误码,因为它们的返回值类型通常是指针。为此,内

一、为什么需要is_err?

在linux内核开发中,内核空间的函数(如内存分配、设备驱动接口)无法像用户空间那样直接返回-enomem-einval等错误码,因为它们的返回值类型通常是指针。为此,内核采用了一种巧妙的方式:将错误码编码到指针值中,而is_err()正是用来检测这种特殊指针的关键工具。

二、is_err的原理

错误码的编码规则

  • 内核将错误码(如-enomem)转换为指针的形式,具体实现依赖于体系结构。
  • 例如在x86-64中,错误码会被转换为(void *)(-4095ul ~ -1ul)区间的虚拟地址(即最高有效位为1的地址)。
  • 当指针值在 0xfffff000~0xffffffffffffffff范围时,is_err()返回true。

核心函数解析

// 判断指针是否为错误码
bool is_err(const void *ptr);

// 从错误指针中提取原始错误码
long ptr_err(const void *ptr);

// 将错误码转换为指针
void *err_ptr(long error);

三、实际使用场景

示例:字符设备驱动中的内存分配

static int mydev_open(struct inode *inode, struct file *filp) {
    struct my_device *dev = kmalloc(sizeof(*dev), gfp_kernel);
    
    if (!dev)
        return -enomem; // 用户空间可以直接返回错误码
    
    // 内核函数返回指针的错误处理
    dev->regs = ioremap(device_base, size);
    if (is_err(dev->regs)) {
        int err = ptr_err(dev->regs);
        kfree(dev);
        return err; // 将错误传递到用户空间
    }
    return 0;
}

四、常见错误及调试技巧

典型错误案例

void *ptr = vmalloc(1024);
if (is_err(ptr)) {  // 错误!vmalloc失败时返回null,而非错误指针
    printk("allocation failed: %ld\n", ptr_err(ptr));
    return;
}

✅ 正确做法:对可能返回错误指针的函数(如devm_clk_get())使用is_err,对返回null的函数(如kmalloc())直接判空。

调试技巧

  • 打印错误码:printk("error code: %ld\n", ptr_err(ptr));
  • 使用dump_stack()定位调用路径

五、底层实现解析( include/linux/err.h)

/* spdx-license-identifier: gpl-2.0 */
#ifndef _linux_err_h
#define _linux_err_h

#include <linux/compiler.h>
#include <linux/types.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 normal
 * pointer with the same return value.
 *
 * this should be a per-architecture thing, to allow different
 * error and pointer decisions.
 */
#define max_errno	4095

#ifndef __assembly__

#define is_err_value(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-max_errno)

static inline void * __must_check err_ptr(long error)
{
	return (void *) error;
}

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

static inline bool __must_check is_err(__force const void *ptr)
{
	return is_err_value((unsigned long)ptr);
}

static inline bool __must_check is_err_or_null(__force const void *ptr)
{
	return unlikely(!ptr) || is_err_value((unsigned long)ptr);
}

/**
 * err_cast - 显式将带有错误值的指针转换为另一种指针类型
 * @ptr: 需要转换的指针。
 *
 * 以一种明确的方式,将带有错误值的指针显式转换为另一种指针类型。
 */
static inline void * __must_check err_cast(__force const void *ptr)
{
	// 去除 const 限定符并进行类型转换
	return (void *) ptr;
}

static inline int __must_check ptr_err_or_zero(__force const void *ptr)
{
	if (is_err(ptr))
		return ptr_err(ptr);
	else
		return 0;
}

#endif

#endif /* _linux_err_h */

当指针值在0xfffff000~0xffffffffffffffff范围时,is_err()返回true

函数/宏功能描述使用场景
ptr_err_or_zero检查指针是否为错误指针,如果是则返回错误码,否则返回 0简化错误处理逻辑,直接获取错误码或成功标志。
err_cast将带限定符的指针转换为普通指针,保留错误信息需要将错误指针传递给期望不同类型的函数时使用。
is_err_or_null检查指针是否为 null 或错误指针同时判断指针是否为空或包含错误信息,适用于返回值可能为 null 或错误指针的情况。

六、总结

场景正确用法错误用法
内存分配失败if (!ptr)is_err(ptr)
资源获取类函数if (is_err(ptr))直接判空
错误传递return ptr_err(ptr);返回未经转换的指针

掌握is_err系列函数的使用,是linux内核调试的重要基础。它不仅能帮助开发者准确定位资源分配错误,更是理解内核错误处理机制的关键入口。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com