Linux深入解析IS_ERR函數(shù)的使用方式
一、為什么需要IS_ERR?
在Linux內(nèi)核開(kāi)發(fā)中,內(nèi)核空間的函數(shù)(如內(nèi)存分配、設(shè)備驅(qū)動(dòng)接口)無(wú)法像用戶空間那樣直接返回-ENOMEM或-EINVAL等錯(cuò)誤碼,因?yàn)樗鼈兊姆祷刂殿愋屯ǔJ侵羔?。為此,?nèi)核采用了一種巧妙的方式:將錯(cuò)誤碼編碼到指針值中,而IS_ERR()正是用來(lái)檢測(cè)這種特殊指針的關(guān)鍵工具。
二、IS_ERR的原理
錯(cuò)誤碼的編碼規(guī)則
- 內(nèi)核將錯(cuò)誤碼(如
-ENOMEM)轉(zhuǎn)換為指針的形式,具體實(shí)現(xiàn)依賴于體系結(jié)構(gòu)。 - 例如在x86-64中,錯(cuò)誤碼會(huì)被轉(zhuǎn)換為
(void *)(-4095UL ~ -1UL)區(qū)間的虛擬地址(即最高有效位為1的地址)。 - 當(dāng)指針值在 0xFFFFF000~0xFFFFFFFFFFFFFFFF范圍時(shí),IS_ERR()返回true。
核心函數(shù)解析
// 判斷指針是否為錯(cuò)誤碼 bool IS_ERR(const void *ptr); // 從錯(cuò)誤指針中提取原始錯(cuò)誤碼 long PTR_ERR(const void *ptr); // 將錯(cuò)誤碼轉(zhuǎn)換為指針 void *ERR_PTR(long error);
三、實(shí)際使用場(chǎng)景
示例:字符設(shè)備驅(qū)動(dòng)中的內(nèi)存分配
static int mydev_open(struct inode *inode, struct file *filp) {
struct my_device *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM; // 用戶空間可以直接返回錯(cuò)誤碼
// 內(nèi)核函數(shù)返回指針的錯(cuò)誤處理
dev->regs = ioremap(DEVICE_BASE, SIZE);
if (IS_ERR(dev->regs)) {
int err = PTR_ERR(dev->regs);
kfree(dev);
return err; // 將錯(cuò)誤傳遞到用戶空間
}
return 0;
}
四、常見(jiàn)錯(cuò)誤及調(diào)試技巧
典型錯(cuò)誤案例
void *ptr = vmalloc(1024);
if (IS_ERR(ptr)) { // 錯(cuò)誤!vmalloc失敗時(shí)返回NULL,而非錯(cuò)誤指針
printk("Allocation failed: %ld\n", PTR_ERR(ptr));
return;
}
? 正確做法:對(duì)可能返回錯(cuò)誤指針的函數(shù)(如devm_clk_get())使用IS_ERR,對(duì)返回NULL的函數(shù)(如kmalloc())直接判空。
調(diào)試技巧
- 打印錯(cuò)誤碼:
printk("Error code: %ld\n", PTR_ERR(ptr)); - 使用
dump_stack()定位調(diào)用路徑
五、底層實(shí)現(xiàn)解析( 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 - 顯式將帶有錯(cuò)誤值的指針轉(zhuǎn)換為另一種指針類型
* @ptr: 需要轉(zhuǎn)換的指針。
*
* 以一種明確的方式,將帶有錯(cuò)誤值的指針顯式轉(zhuǎn)換為另一種指針類型。
*/
static inline void * __must_check ERR_CAST(__force const void *ptr)
{
// 去除 const 限定符并進(jìn)行類型轉(zhuǎn)換
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 */
當(dāng)指針值在0xFFFFF000~0xFFFFFFFFFFFFFFFF范圍時(shí),IS_ERR()返回true。


| 函數(shù)/宏 | 功能描述 | 使用場(chǎng)景 |
|---|---|---|
| PTR_ERR_OR_ZERO | 檢查指針是否為錯(cuò)誤指針,如果是則返回錯(cuò)誤碼,否則返回 0 | 簡(jiǎn)化錯(cuò)誤處理邏輯,直接獲取錯(cuò)誤碼或成功標(biāo)志。 |
| ERR_CAST | 將帶限定符的指針轉(zhuǎn)換為普通指針,保留錯(cuò)誤信息 | 需要將錯(cuò)誤指針傳遞給期望不同類型的函數(shù)時(shí)使用。 |
| IS_ERR_OR_NULL | 檢查指針是否為 NULL 或錯(cuò)誤指針 | 同時(shí)判斷指針是否為空或包含錯(cuò)誤信息,適用于返回值可能為 NULL 或錯(cuò)誤指針的情況。 |


六、總結(jié)
| 場(chǎng)景 | 正確用法 | 錯(cuò)誤用法 |
|---|---|---|
| 內(nèi)存分配失敗 | if (!ptr) | IS_ERR(ptr) |
| 資源獲取類函數(shù) | if (IS_ERR(ptr)) | 直接判空 |
| 錯(cuò)誤傳遞 | return PTR_ERR(ptr); | 返回未經(jīng)轉(zhuǎn)換的指針 |
掌握IS_ERR系列函數(shù)的使用,是Linux內(nèi)核調(diào)試的重要基礎(chǔ)。它不僅能幫助開(kāi)發(fā)者準(zhǔn)確定位資源分配錯(cuò)誤,更是理解內(nèi)核錯(cuò)誤處理機(jī)制的關(guān)鍵入口。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- linux之errno與signum:錯(cuò)誤碼與信號(hào)詳解
- Linux報(bào)錯(cuò):tar: Error Is Not Recoverable: Exiting Now問(wèn)題及解決
- Linux Shell腳本syntax error: unexpected end of file原因及解決
- linux上運(yùn)行python腳本,SyntaxError:?invalid?syntax的解決
- 詳解C/C++ Linux出錯(cuò)處理函數(shù)(strerror與perror)的使用
- linux或windows環(huán)境下pytorch的安裝與檢查驗(yàn)證(解決runtimeerror問(wèn)題)
- Linux使用vim編輯文件保存時(shí)報(bào)E514:write error (file system full?)問(wèn)題解決
相關(guān)文章
紅帽RHEL8和7的區(qū)別對(duì)比分享(Centos8與7參照redhat)
這篇文章主要介紹了紅帽RHEL8和7有什么區(qū)別(Centos8與7參照redhat),包括紅帽RHEL8和RHEL7功能區(qū)別對(duì)比和RHEL8額外新功能新特性,對(duì)紅帽RHEL8和7相關(guān)知識(shí)感興趣的朋友跟隨小編一起看看吧2023-01-01
LINUX服務(wù)器安裝SVN服務(wù)實(shí)現(xiàn)方式
本文介紹了如何使用yum安裝Subversion,創(chuàng)建版本庫(kù),配置SVN服務(wù),并解決常見(jiàn)問(wèn)題,詳細(xì)步驟包括安裝Subversion,查看安裝版本和位置,創(chuàng)建存放版本庫(kù)的目錄及svn版本庫(kù),配置權(quán)限控制,啟動(dòng)svn版本庫(kù),以及處理端口訪問(wèn)權(quán)限等2024-09-09
解決Debian系統(tǒng)自動(dòng)更新軟件包的問(wèn)題的方法
這篇文章主要介紹了解決Debian系統(tǒng)自動(dòng)更新軟件包的問(wèn)題的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Linux中行、列顯示與轉(zhuǎn)換實(shí)現(xiàn)方式
本文介紹了在Linux中如何將多項(xiàng)數(shù)據(jù)或內(nèi)容打印輸出為一行,用空格或其他分隔符分開(kāi),以及如何將輸出按列顯示,同時(shí),還介紹了如何使用xargs、sed和awk等命令將行列輸出進(jìn)行轉(zhuǎn)換2025-11-11
Linux VPS備份教程 數(shù)據(jù)庫(kù)/網(wǎng)站文件自動(dòng)定時(shí)備份
這篇文章老左分享目前大家比較常用的自動(dòng)定時(shí)備份方案,數(shù)據(jù)庫(kù)發(fā)送到我們的郵箱、文件發(fā)送同步到我們另外空間FTP中2012-09-09
Linux 文件系統(tǒng)的操作實(shí)現(xiàn)
這篇文章主要介紹了Linux 文件系統(tǒng)的操作實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
linux安裝mysql數(shù)據(jù)庫(kù)以及配置Java項(xiàng)目的圖文詳解
在本篇文章里小編給大家整理的是關(guān)于linux安裝mysql配置Java的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們可以跟著學(xué)習(xí)下。2019-11-11
Linux磁盤分區(qū)實(shí)戰(zhàn)案例(必看篇)
下面小編就為大家?guī)?lái)一篇Linux磁盤分區(qū)實(shí)戰(zhàn)案例(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12

