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


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


六、總結(jié)
| 場景 | 正確用法 | 錯誤用法 |
|---|---|---|
| 內(nèi)存分配失敗 | if (!ptr) | IS_ERR(ptr) |
| 資源獲取類函數(shù) | if (IS_ERR(ptr)) | 直接判空 |
| 錯誤傳遞 | return PTR_ERR(ptr); | 返回未經(jīng)轉(zhuǎn)換的指針 |
掌握IS_ERR系列函數(shù)的使用,是Linux內(nèi)核調(diào)試的重要基礎。它不僅能幫助開發(fā)者準確定位資源分配錯誤,更是理解內(nèi)核錯誤處理機制的關鍵入口。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- linux之errno與signum:錯誤碼與信號詳解
- Linux報錯:tar: Error Is Not Recoverable: Exiting Now問題及解決
- Linux Shell腳本syntax error: unexpected end of file原因及解決
- linux上運行python腳本,SyntaxError:?invalid?syntax的解決
- 詳解C/C++ Linux出錯處理函數(shù)(strerror與perror)的使用
- linux或windows環(huán)境下pytorch的安裝與檢查驗證(解決runtimeerror問題)
- Linux使用vim編輯文件保存時報E514:write error (file system full?)問題解決
相關文章
解決linux系統(tǒng)中運行node進程卻無法殺死進程的問題
這篇文章主要介紹了linux系統(tǒng)中運行node進程無法殺死進程的問題,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02

