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

 更新时间:2025年07月11日 09:47:34   作者:小嵌同学  
文章解释了Linux内核中IS_ERR函数的作用及原理,说明其通过检测特定地址范围的指针来识别错误码,用于内存分配和资源获取等场景,并给出调试技巧与底层宏实现解析

一、为什么需要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内核调试的重要基础。它不仅能帮助开发者准确定位资源分配错误,更是理解内核错误处理机制的关键入口。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 红帽RHEL8和7的区别对比分享(Centos8与7参照redhat)

    红帽RHEL8和7的区别对比分享(Centos8与7参照redhat)

    这篇文章主要介绍了红帽RHEL8和7有什么区别(Centos8与7参照redhat),包括红帽RHEL8和RHEL7功能区别对比和RHEL8额外新功能新特性,对红帽RHEL8和7相关知识感兴趣的朋友跟随小编一起看看吧
    2023-01-01
  • Ubuntu20.04修改ip地址的方法示例

    Ubuntu20.04修改ip地址的方法示例

    这篇文章主要介绍了Ubuntu20.04修改ip地址的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • LINUX服务器安装SVN服务实现方式

    LINUX服务器安装SVN服务实现方式

    本文介绍了如何使用yum安装Subversion,创建版本库,配置SVN服务,并解决常见问题,详细步骤包括安装Subversion,查看安装版本和位置,创建存放版本库的目录及svn版本库,配置权限控制,启动svn版本库,以及处理端口访问权限等
    2024-09-09
  • 解决Debian系统自动更新软件包的问题的方法

    解决Debian系统自动更新软件包的问题的方法

    这篇文章主要介绍了解决Debian系统自动更新软件包的问题的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Linux中行、列显示与转换实现方式

    Linux中行、列显示与转换实现方式

    本文介绍了在Linux中如何将多项数据或内容打印输出为一行,用空格或其他分隔符分开,以及如何将输出按列显示,同时,还介绍了如何使用xargs、sed和awk等命令将行列输出进行转换
    2025-11-11
  • Linux VPS备份教程 数据库/网站文件自动定时备份

    Linux VPS备份教程 数据库/网站文件自动定时备份

    这篇文章老左分享目前大家比较常用的自动定时备份方案,数据库发送到我们的邮箱、文件发送同步到我们另外空间FTP中
    2012-09-09
  • Linux 文件系统的操作实现

    Linux 文件系统的操作实现

    这篇文章主要介绍了Linux 文件系统的操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • linux安装mysql数据库以及配置Java项目的图文详解

    linux安装mysql数据库以及配置Java项目的图文详解

    在本篇文章里小编给大家整理的是关于linux安装mysql配置Java的相关知识点内容,有需要的朋友们可以跟着学习下。
    2019-11-11
  • Linux磁盘分区实战案例(必看篇)

    Linux磁盘分区实战案例(必看篇)

    下面小编就为大家带来一篇Linux磁盘分区实战案例(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Linux之nfs与autofs的使用方式

    Linux之nfs与autofs的使用方式

    这篇文章主要介绍了Linux之nfs与autofs的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-08-08

最新评论