【Linux内核】Linux的errno和ERR_PTR、PTR_ERR简介

1. Linux的errno和指针

常见的Linux函数返回值类型包括整型和指针,内核中这两种返回值类型的函数会互相调用。Linux的errno为整型,为了与errno对应,Linux将指针分为3种:

  • 空指针:NULL,地址为0
  • 异常指针:地址空间的高4095字节。0xfffff001 - 0xffffffff(32位)和0xfffffffffffff001 - 0xffffffffffffffff(64位)
  • 普通指针

errno的最大值MAX_ERRNOerr.h定义,值为4095。关于变量转换类型后的值,可以使用测试程序来进行测试。

longvoid *(arm)unsigned long(arm)void *(arm64)unsigned long(arm64)
0(nil)0(nil)0
-10xffffffff0xFFFFFFFF0xffffffffffffffff0xFFFFFFFFFFFFFFFF
-MAX_ERRNO0xfffff0010xFFFFF0010xfffffffffffff0010xFFFFFFFFFFFFF001

为了处理两种数据类型的转换,内核提供了include/linux/err.h

1.1. 整型和指针类型转换

interfaceinputoutputfunction
IS_ERR_VALUEanybool强制转换为unsigned long后,判断是否大于(unsigned long)-MAX_ERRNO
ERR_PTRlongvoid *errno转指针
PTR_ERRpointerlong指针转errno
IS_ERRpointerbool判断指针是否异常
IS_ERR_OR_NULLpointerbool判断指针是否异常或NULL
ERR_CASTpointervoid *将任意类型的指针转为void型指针
PTR_ERR_OR_ZEROpointerint如果是异常指针,返回指针对应的errno,否则返回0

2. C语言中整型数据的存储和数据类型转换原理

其实,在C语言中,数值是以补码的形式存储的,正数存储的内容就是其本身(原码),负数补码的计算方法如下:

  1. 取绝对值
  2. 取反码,也就是对每一位取反
  3. 对反码加1,得到补码

以char型为例,-1在内存中的表示计算方法如下:

取反码
反码加1
0000 0001
1111 1110
1111 1111

关于为什么使用补码存储数据,可以参整数在内存中是如何存储的,为什么它堪称天才般的设计

2.1. 负数的强制类型转换

做强制类型转换时,就是把内存中的数据(补码)看作要转换的类型。将-1强制转换为unsigned char型时,就可得到值为0xFF,以此类推,可知将-转换为unsigned long时,值为0xFFFFFFFF(32位)或0xFFFFFFFFFFFFFFFF(64位)。同样的道理-4095转换为unsigned long时,值为0xFFFFF001(32位)或0xFFFFFFFFFFFFF001(64位)。需要注意的是,类型转换是临时的,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。

2.2. 强制数据类型降级

数据类型级别从高到低为:

unsigned long long	
long long	
unsigned long	
long	
unsigned int	
int
unsigned short
short
unsigned char
char

从高级到低级转换时,超出数值位数的高位部分将被丢弃。另外,降级转换也是是临时的。

3. 测试程序

#include <stdio.h>
#include <string.h>

#define MAX_ERRNO   4095

union err_t {
    int i;
    long l;
    unsigned int ui;
    unsigned long ul;
    void *p;
    char ch[sizeof(unsigned long)];
};

static void test(long val)
{
    union err_t err;
    memcpy(&err, &val, sizeof(val));

    printf("int:           %d\n", err.i);
    printf("long:          %ld\n", err.l);
    printf("unsigned int:  %u, 0x%08X\n", err.ui, err.ui);
    printf("unsigned long: %lu, 0x%08lX\n", err.ul, err.ul);
    printf("void *:        %p\n", err.p);
}

int main(void)
{
    printf("\ntesting: 0\n");
    test(0);

    printf("\ntesting: -1\n");
    test(-1);

    printf("\ntesting: -MAX_ERRNO\n");
    test(-MAX_ERRNO);

#if 0
    printf("\ntesting: -4096\n");
    test(-4096);
#endif
    return 0;
}

3.1. ARM测试结果

交叉编译后使用QEMU运行查看结果。

arm-linux-gnueabihf-gcc -static -o err_arm err.c
qemu-arm err_arm
testing: 0
int:           0
long:          0
unsigned int:  0, 0x00000000
unsigned long: 0, 0x00000000
void *:        (nil)

testing: -1
int:           -1
long:          -1
unsigned int:  4294967295, 0xFFFFFFFF
unsigned long: 4294967295, 0xFFFFFFFF
void *:        0xffffffff

testing: -MAX_ERRNO
int:           -4095
long:          -4095
unsigned int:  4294963201, 0xFFFFF001
unsigned long: 4294963201, 0xFFFFF001
void *:        0xfffff001

3.2. ARM64测试结果

交叉编译后使用QEMU运行查看结果。

aarch64-linux-gnu-gcc -static -o err_aarch64 err.c
qemu-aarch64 err_aarch64
testing: 0
int:           0
long:          0
unsigned int:  0, 0x00000000
unsigned long: 0, 0x00000000
void *:        (nil)

testing: -1
int:           -1
long:          -1
unsigned int:  4294967295, 0xFFFFFFFF
unsigned long: 18446744073709551615, 0xFFFFFFFFFFFFFFFF
void *:        0xffffffffffffffff

testing: -MAX_ERRNO
int:           -4095
long:          -4095
unsigned int:  4294963201, 0xFFFFF001
unsigned long: 18446744073709547521, 0xFFFFFFFFFFFFF001
void *:        0xfffffffffffff001
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值