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()实现的更多相关文章

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

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

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

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

  3. Linux内核中流量控制

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

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

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

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

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

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

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

  7. Linux 内核中的 Device Mapper 机制

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

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

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

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

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

随机推荐

  1. Java开发环境系列:一篇能解决你99%问题的排雷日记

      安装 https://archive.apache.org/dist/tomcat/ 推荐使用免安装版的Tomcat(放在没有中文和空格的目录下),前提是已经安装了JDK并配置了环境变量.Linu ...

  2. Delphi中窗体的事件

    Delphi中窗体的事件 Form窗体可以响应各种各样的时间,在Object Inspector的Events页面中罗列了一大堆,如下图: 下面将要列出一些常用的事件. 1.OnActivate 当窗 ...

  3. <Android Studio> 2.APP开机启动

    开机启动,也就是App随着机器开机而启动,在很多工业场景中是非常常见的. 开机启动的基本原理就是监听系统启动相关的广播,然后启动App. 为了实现开机启动,我人为的分为几个步骤 1.创建broadca ...

  4. flink 实现ConnectedComponents 连通分量,增量迭代算法(Delta Iteration)实现详解

    1.连通分量是什么? 首先需要了解什么是连通图.无向连通图.极大连通子图等概念,这些概念都来自数据结构-图,这里简单介绍一下. 下图是连通图和非连通图,都是无向的,这里不扩展有向图: 连通分量(con ...

  5. 01篇ELK日志系统——升级版集群之elasticsearch集群的搭建

    [ 前言:以前搭了个简单的ELK日志系统,以我个人的感觉来说,ELK日志系统还是非常好用的.以前没有弄这个ELK日志系统的时候,线上的项目出了bug,报错了,要定位错误是什么,错误出现在哪个java代 ...

  6. 掌握 Async/Await

    摘要: 还不用Async/Await就OUT了.. 原文:掌握 Async/Await 作者:Jartto Fundebug经授权转载,版权归原作者所有. 前端工程师肯定都经历过 JS 回调链狱的痛苦 ...

  7. 为什么内核访问用户数据之前,要做access_ok?

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 原理 先看一段小视频,如果内核访问用户 ...

  8. linux命令:set 指定行,直接替换并修改文件

    sed 命令: 指定行,从第一行到第一行: 把该行的ssd,换成cd: -i 表示的是替换并直接修改文件: sed  -i  '1,1s/ssd/cd/g' test_file 命令使用: sed - ...

  9. sublime ctrl b突然不能用解决方法

    Sublime Text 2 ?ctrl+b 如果出现运行为空白,按ctrl+`来显示错误,如下所示,转载了一篇解决方案 ? 文章参考:http://eric.themoritzfamily.com/ ...

  10. 简述 asynio模块的作用和应用场景。

    asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持. asyncio的编程模型就是一个消息循环.我们从asyncio模块中直接获取一个EventLoop的引用, 然后把需 ...