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. 模板模式创建一个poi导出功能

    之前的导出都很乱,直接写在代码中,等到下回还使用导出功能时又不知如何下手,今天用模板模式重写了一个导出功能,方便以后使用: package com.sf.addrCheck.util.export.p ...

  2. Lisp : (quote) code is data (eval) data as code

  3. windows 2012 IIS 部署 .net core HTTP Error 502.5 - Process Failure 错误解决办法

    安装系统补丁: 2012 R2对应>Windows8.1-KB2999226-x64.msu 下载地址: https://www.microsoft.com/zh-CN/download/det ...

  4. nginx配置ssl证书实现https

    一,环境说明 服务器系统:centos6 服务器IP地址:0.0.0.0 域名:bjubi.com 二,域名解析到服务器 在阿里云控制台-产品与服务-云解析DNS-找到需要解析的域名点“解析”,进入解 ...

  5. Vue.js项目实战-打造线上商城

    首先上一下完成后的效果: 首页: 商品详情页: 购物车页(其实还有个订单页,只是和购物车页基本类似,所以就不截图啦): 开始项目: 由于涉及的是前后端分离,所以我们的后台数据就模拟存储于浏览器端(co ...

  6. 设计模式之(十一)代理模式(Proxy)

    软件开发行业有一个观点:任务问题都可以添加一个中间层来解决.代理模式也是这个思想下的产物. 首先看下代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.就是把类委托给另外一个类,用这个类来控 ...

  7. django rest_framework vue 实现用户列表分页

    django rest_framework vue 实现用户列表分页 后端 配置urls # 导入view from api.appview.userListView import userListV ...

  8. Pods应用NFS存储

    Volumes选择NFS服务器: 条件: 1. k8s集群,目前为(hadoop1,hadoop2,hadoop3) 2. 另起一台服务器做专门的NFS服务器 3. NFS需要在K8S的各个节点安装 ...

  9. CentOS6.7编译安装mysql5.5(详解编译选项)

    注意!  mysql5.5之前一般都是用make编译 mysql5.5 -5.6 一般都是用cmake编译 cmake : 跨平台编译器, mysql官方提供的rpm包 mysql-client :提 ...

  10. LCD裸板编程_框架

    lcd程序框架: 为了让程序更好的扩展,介绍面向对象编程: 比如抽象出lcd_3.5和lcd_4.3的共同点: 当我想使用3.5寸的lcd时,只需让lo指向lcd_3.5_opr即可.同样,当我想使用 ...