linux内核err实现,Linux内核中的IS_ERR()实现

1、前言

对于任何一个指针来说,必然有三种情况:一种是有效指针,一种是NULL,也就是空指针,一种是错误指针,也就是无效指针,在Linux内核中,所谓的错误指针就是指其已经到达了内核空间的最后一个page,例如,对于32bit的系统来说,内核空间最后地址为0xFFFF FFFF,那么最后一个page就是指地址0xFFFF F000~0xFFFF FFFF(4K大小页面),这段地址是被保留的,如果指针落在这段地址之内,说明是错误的无效的指针。

2、内核如何实现IS_ERR()

在Linux内核源码中实现了指针错误的处理机制,相关的函数接口主要有IS_ERR()、PTR_ERR()、ERR_PTR()等,其函数的源码在include/linux/err.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);

}

在Linux的源码中IS_ERR()函数其实就是判断指针是否出错,如果指针指向了内核空间的最后一个page,那么说明它就是一个无效的指针,如果指针并不是落在内核空间的最后一个page,那么说明这指针是有效的,内核中,无效的指针能表示成一种负数的错误号。

内核空间为什么要保留出最后一个page呢?首先,驱动程序都是运行在内核空间的,内核空间虽然很大,但总是有限的,在这有限的空间内,最后一个page是专门保留的,一般人并不可能会用到内核空间最后一个page的指针,其次,内核空间中返回的指针一般是指向页面的边界(4K边界),也就是ptr & 0xFFF == 0,当发现指针指向了最后一个page,哪说明该指针是无效的。

接下来,对IS_ERR()函数进行分析,其函数如下所示:

static inline bool __must_check IS_ERR(__force const void *ptr)

{

return IS_ERR_VALUE((unsigned long)ptr);

}

在该函数的内部,调用了IS_ERR_VALUE(),这是一个宏定义,其代码如下:

#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

在源码中可以知道MAX_ERRNO的值时4095,也就是0xFFF,在上面的宏定义中(unsigned long)-MAX_ERRNO,是对负数-MAX_ERRNO进行强制类型转换,-0xFFF强制转换为unsigned long类型为0xFFFF F000,所以该宏等价于下面:

#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (0xFFFFF000))

也就是判断传入的指针值是否落在区间0xFFFF F000~0xFFFF FFFF之内,如果落在这个区间之内的话,就是无效的指针,因此,可以使用IS_ERR()函数去判断内核函数中返回的指针值是否是有效的指针,另外,平时在内核中看见的错误号码都是在前面加个负号,也就是这个原因。

ERR_PTR()和PTR_ERR()函数只是对错误进行强制转换而已,PTR_ERR()函数将无效指针转换为错误号,ERR_PTR()函数将错误号转换为无效指针,而IS_ERR_OR_NULL()函数则用来判断传入的指针是无效指针还是空指针。

3、使用示例

对于IS_ERR()的使用,可以参考下面的代码:

myclass = class_create(THIS_MODULE, "myclass");

if (IS_ERR(myclass)) {

ret = PTR_ERR(myclass);

goto fail;

}

mydevice = device_create(myclass, NULL, MKDEV(major, ), NULL, "simple-device");

if (IS_ERR(mydevice)) {

class_destroy(myclass);

ret = PTR_ERR(mydevice);

goto fail;

}

在代码中,调用class_create()和device_create()函数,必须使用IS_ERR()函数判断返回的指针是否是有效的,如果是无效的,需要调用PTR_ERR()函数将无效的指针转换为错误号,并进行错误号返回。

4、小节

本文主要简单介绍了Linux内核中无效指针的处理机制,包括IS_ERR()、PTR_ERR()等函数的实现。

参考:

https://blog.csdn.net/xxu0123456789/article/details/6339625

https://blog.csdn.net/ljk0922/article/details/47911203

https://www.cnblogs.com/Ph-one/p/4414540.html

https://blog.csdn.net/u014470361/article/details/81175817

linux内核中的IS_ERR()、PTR_ERR()、ERR_PTR()

IS_ERR宏定义在include/linux/err.h,如下所示: #define MAX_ERRNO 4095 //判断x是不是在(0xfffff000,0xffffffff)之间,注意这里用u ...

Linux内核中的GPIO系统之(3):pin controller driver代码分析

一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

Linux内核中流量控制

linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...

【转】 Linux内核中读写文件数据的方法--不错

原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法  有时候需要在Linuxkernel--大 ...

【转】在linux内核中读写文件 -- 不错

原文网址:http://blog.csdn.net/tommy_wxie/article/details/8194276 1. 序曲 在用户态,读写文件可以通过read和write这两个系统调用来完成 ...

Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...

Linux 内核中的 Device Mapper 机制

本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

向linux内核中添加外部中断驱动模块

本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

Linux内核中双向链表的经典实现

概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

随机推荐

JavaScript Math和Number对象

目录 1. Math 对象:数学对象,提供对数据的数学计算.如:获取绝对值.向上取整等.无构造函数,无法被初始化,只提供静态属性和方法. 2. Number 对象 :Js中提供数字的对象.包含整数.浮 ...

[NHibernate]N+1 Select查询问题分析

目录 写在前面 文档与系列文章 N+1 Select查询问题分析 总结 写在前面 在前面的文章(延迟加载,立即加载)中都提到了N+1 Select的问题,总觉得理解的很不到位,也请大家原谅,这也是为什 ...

Redis 配置文件详解

# Redis 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写)## 1k => 1000 bytes# 1kb =&gt ...

java基础回顾(八)——Queue

今天回顾了下关于Queue的一些相关知识 我们可以看到,Deque也是一个接口,它继承了Queue的接口规范.其中LinkedList和ArrayDeque都是实现Deque接口,所以,可以说他们俩都 ...

html5新增标签兼容性

很多低版本的浏览器是不识html5新增的标签的,所以为了解决浏览器兼容性问题,主要有两种方法: js可以创建我们自定义的标签,例如,我们可以用js语句 document.createElement(' ...

php中实现精确设置session过期时间的方法

http://www.jb51.net/article/52309.htm 大多数据情况下我们对于session过期时间使用的是默认设置的时间,而对于一些有特殊要求的情况下我们可以设置一下sessio ...

Java父类子类的对象初始化过程

摘要 Java基本的对象初始化过程,子类的初始化,以及涉及到父类和子类的转化时可能引起混乱的情况. 1. 基本初始化过程: 对于一个简单类的初始化过程是: static 修饰的模块(static变量和 ...

LODOP获取打印成功,是否加入队列

之前博文介绍过获取打印机状态码 LODOP获取打印机状态码和状态码含义测试,但是打印机种类千差万别,状态码不一定准确,特别是打印成功的状态码,获取任务不在队列,可以判断打印成功,删除任务也是任务不在队 ...

springboot+mybatis+pagehelper

springboot+mybatis+pagehelper整合 springboot   版本2.1.2.RELEASE mybatis  版本3.5 pagehelper 版本5.18 支持在map ...

[加密]openssl之数字证书签名,CA认证原理及详细操作

转自:http://blog.sina.com.cn/s/blog_cfee55a70102wn3h.html 1 公钥密码体系(Public-key Cryptography) 公钥密码体系,又称非 ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值