Linux内核错误码和错误指针

1.IS_ERR_VALUE

Linux内核中,最大错误码的值为MAX_ERRNO,错误码为负数,负数以补码的形式存储。Arm32位系统错误码的补码范围为0xFFFFF000 - 0xFFFFFFFF。Arm64系统错误码的补码范围为0xFFFFFFFFFFFFF000- 0xFFFFFFFFFFFFFFFF。
Arm32位系统Linux内核的虚拟地址范围:0xC0000000 - 0xFFFFFFFF,最后一页(页大小为4KB)为预留地址,即0xFFFFF000 - 0xFFFFFFFF地址范围为预留地址,若内核地址大于0xFFFFF000,则为非法地址。Arm64位系统Linux内核的虚拟地址范围:0xFFFF000000000000 - 0xFFFFFFFFFFFFFFFF,高16为全为1。最后一页(页大小为4KB)为预留地址,即0xFFFFFFFFFFFFF000- 0xFFFFFFFFFFFFFFFF地址范围为预留地址,若内核地址大于0xFFFFFFFFFFFFF000,则为非法地址。
IS_ERR_VALUE可判断传入的数值是否是错误码,一般比较少直接使用,注意IS_ERR_VALUE不能判断空指针。

[include/linux/err.h]
#define MAX_ERRNO        4095
#define IS_ERR_VALUE(x) 
    unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

2.ERR_PTR

ERR_PTR将传入的错误码转换为指针。某些函数的返回值是一个指针,但需要返回一个负的错误码,以表明错误发生的原因,可使用ERR_PTR宏将负的错误码转换为指针返回。调用函数可用IS_ERR判断该指针是否是错误码,若是错误码,可使用PTR_ERR宏将指针转为错误码。

[include/linux/err.h]
static inline void * __must_check ERR_PTR(long error)
{
    return (void *) error;
}

下面是ERR_PTRIS_ERRPTR_ERR的使用案例。ERR_PTR-ENOMEM错误码转换为指针返回。IS_ERR判断指针是否是错误码,PTR_ERR将针转换为错误码。

[drivers/input/touchscreen/ads7846.c]
static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
{
    ......
    pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    if (!pdata)
        return ERR_PTR(-ENOMEM);  // 将错误码转换为指针
    ......
}
static int ads7846_probe(struct spi_device *spi)
{
    ......
    pdata = ads7846_probe_dt(&spi->dev);
    if (IS_ERR(pdata)) {       // 判断指针是否是错误码
        err = PTR_ERR(pdata);  // 将指针转换为错误码
        goto err_free_mem;
    }
    ......
}

3.IS_ERR

IS_ERR判断传入的指针是否是错误码,内部调用了IS_ERR_VALUE宏,案例见1.2节。

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

4.IS_ERR_OR_NULL

IS_ERR_OR_NULL判断传入的指针是否是空指针或错误码。在IS_ERR_VALUE宏的基础上,增加了空指针的判断。

[include/linux/err.h]
static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
{
    return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr);
}

likelyunlikely内部封装了GCC的内建函数__builtin_expect,用来告诉编译器分支跳转信息。likely表示结果很可能为真,unlikely表示结果很可能为假,编译器得到此信息后会做相应的优化,以减少跳转造成的性能损失。__builtin_expect第一个参数是要判断的值,第二个参数表示期望的结果,1为真。0为假。

[include/linux/compiler.h]
# define likely(x)      __builtin_expect(!!(x), 1)  // 很有可能为真
# define unlikely(x)    __builtin_expect(!!(x), 0)  // 很有可能为假

5.ERR_CAST

ERR_CAST将错误码转换的指针类型转换为void*类型。经常用在错误码转换的指针类型和函数返回指针类型不一致的场景中。避免编译器报警告。

[include/linux/err.h]
static inline void *__must_check ERR_CAST(__force const void *ptr)
{
    return (void *) ptr;  /* cast away the const */
}

下面是ERR_CAST使用案例。

[include/linux/pinctrl/consumer.h]
static inline struct pinctrl * __must_check devm_pinctrl_get_select(
    struct device *dev, const char *name)
{
    struct pinctrl_state *s;  // s的类型和返回值类型不一样
    ......
    s = pinctrl_lookup_state(p, name);
    if (IS_ERR(s)) {         // 判断返回的是不是错误码
        devm_pinctrl_put(p);
        return ERR_CAST(s);  // 将指针转换为void*类型返回 
    }
    ......
}
[drivers/pinctrl/core.c]
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
        const char *name)
{
    struct pinctrl_state *state;
    state = find_state(p, name);
    if (!state) {
        ......
        state = ERR_PTR(-ENODEV);  // 将错误码转换为指针返回
    }
    return state;
}

6.PTR_ERR_OR_ZERO

PTR_ERR_OR_ZERO判断传入的指针是否是错误码,若是,则将指针转换为错误码返回,若不是,则返回0。

[include/linux/err.h]
static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
{
    if (IS_ERR(ptr))
        return PTR_ERR(ptr);
    else
        return 0;
}

你可能感兴趣的:(Linux相关知识,Linux,内核,PTR_ERR,错位码)