解读PTR_ERR,ERR_PTR,IS_ERR

看到了几个宏PTR_ERR,ERR_PTR,IS_ERR(其实是内联函数).还是不太明白,然后就google搜索了一下,搜出来的结果真是不让人满意,看完一些解释我更迷糊了。看来还得依靠内核源码,依靠对内核的理解自己弄明白了。大致看了一下这几个宏的定义还有在内核的用法,恍然大悟。原来这几个宏这么简单,原理也这么简单。下面就说一下这几个宏的由来与用处。

我们知道内核有些函数是返回指针的,如Kmalloc分配内存,如果分配不到内核就会返回NULL指针,这样我们可以通过判断是否是NULL指针来判断Kmalloc执行成功与否。但是有些函数返回错误时,我们不仅需要知道函数错了,还需要知道错在哪里了,也就是说我们要或得错误码。在用户空间编程的时候,因为每个线程都有一个error变量,我们可以通过访问这个变量来得到错误码。但是在内核就没有这个变量,所以不能这样或得函数执行的错误码。那么聪明的内核开发着就根据内核的地址空间特点用了一种新的方法来或得错误码,那就是PTR_ERR,ERR_PTR,IS_ERR这三个宏,暂时先不说这三个宏怎么用,我们来看一下错误码与内核地址空间的特点。

基本的错误码在内核中的errno-base.h中,如下:


#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
。。。。。。。 #define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */

在不同的体系结构中也有一些err的宏定义,但是内核规定总的大小不能超过4095.说完了e错误码,然后在说一下内核的地址空间。我们知道Linux是基于虚拟内存的内核,所以CPU访问的是线性地址,而线性地址需要通过页表来转化成物理地址,如若一个线性地址的页表不存在的话会发生缺页异常。Linux在内核地址空间映射了大于0xc0000000的所有可用线性地址,而对于小于0xc0000000的线性地址在内核态是没有页表的,内核也从不使用小于0xc0000000的线性地址。也就是说内核返回指针的函数,如果执行正确,他返回的指针的大小绝对不会小于0xc0000000。如果小于这个那么肯定是错误的。所以可以利用这一点。内核函数都遵守一个约定,那就是如果不能返回正确的指针,那么就返回错误的,我们把返回的错误指针作为错误码。因为错误码都是整数,而返回的是指针,所以需要强制转换一下,这就诞生了这三个宏PTR_ERR,ERR_PTR,IS_ERR。这三个宏(内联函数)的定义在err.h中

#define MAX_ERRNO   4095  

#ifndef __ASSEMBLY__  

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

static inline void *ERR_PTR(long error)
{
return (void *) error;
} static inline long PTR_ERR(const void *ptr)
{
return (long) ptr;
} static inline long IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}

判断是否为错误指针也是很简单unlikely((x) >= (unsigned long)-MAX_ERRNO),这里问什么是大于简单说一下,以为错误码在返回的时候都去负数了,负数大的他的绝对值就小了。就是这个道理。至于这里为什么是4095,那就是内核约定的了,注意这里与什么页面大小没有一点关系,内核完全可以约定0xbfffffff,也是可以的,因为小于0xc0000000的线性地址都是错误的。这三个宏这样用。首先是一个返回指针的内核函数,比如 :

struct device *foo()
{
...
if(...) {//错误了
return ERR_PTR(-EIO);
}
}

我们在调用这个函数的时候:

struct device *d;
d = foo();
if (IS_ERR(d)) {
long err = PTR_ERR(d);
printk("errno is %d\n", err);
}

这样就可以提取错误码,然后根据错误码再做什么处理就由具体的驱动来处理了。我感觉其实将内核的机构与原理理解清楚了,内核的一些技巧就非常好理解了。

TEST

ERR_PTR : 这里PTR是pointer的意思

PTR_ERR

IS_ERR

#cat hello.c
// Defining __KERNEL__ and MODULE allows us to access kernel-level code not usually available to userspace programs.
#undef __KERNEL__
#define __KERNEL__ #undef MODULE
#define MODULE // Linux Kernel/LKM headers: module.h is needed by all modules and kernel.h is needed for KERN_INFO.
#include <linux/module.h> // included for all kernel modules
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/err.h> struct student {
char *name;
int age;
}; struct student *func1(void)
{
if (1) {
printk(KERN_INFO "call func1()\n");
return ERR_PTR(-EIO); // 一般都是func1()的返回值是指针,这个时候,需要ERR_PTR,将errno转换成pointer
}
} static int __init hello_init(void)
{
printk(KERN_INFO "Hello world!\n"); struct student *p; p = func1();
if (IS_ERR(p)) { //IS_ERR()其实是在判断指针
long err = PTR_ERR(p); //再将指针转换成long err
printk(KERN_INFO "errno is %ld\n", err);
} return 0; // Non-zero return means that the module couldn't be loaded.
} static void __exit hello_cleanup(void)
{
printk(KERN_INFO "Cleaning up module.\n");
} module_init(hello_init);
module_exit(hello_cleanup);
#cat Makefile
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd) all:
$(MAKE) -C $(KDIR) M=$(PWD) modules clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

执行make

解读PTR_ERR,ERR_PTR,IS_ERR的更多相关文章

  1. ERR_PTR,PTR_ERR还有IS_ERR函数详解

    内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来. 总体来说,如果内核返回一个指针,那么有三种情况:合法指针,NULL指针和非法指针. 1)合法指针:内核返回的指针一般是指向 ...

  2. IS_ERR、PTR_ERR、ERR_PTR

    最近在使用filp_open打开文件时遇到到一个问题,当打开一个并不存在的文件时,filp_open返回值值为0xfffffffe,而并不是0(NULL),这是因为内核对返回指针的函数做了特殊处理.内 ...

  3. ERR_PTR PTR_ERR IS_ERR ERROR

    在linux-x.xx/include/uapi/asm-generic/errno-base.h和errno.h里分别定义了返回错误的信息. errno-base.h: #ifndef _ASM_G ...

  4. 内核IS_ERR宏解析 【转】

    转自:http://blog.chinaunix.net/uid-20196318-id-28769.html 最近在使用filp_open打开文件时遇到到一个问题,当打开一个并不存在的文件时,fil ...

  5. IS_EER分析

    下面我们就来具体分析一下这段代码,看看内核中的巧妙设计思路. 要想明白IS_ERR(),首先理解要内核空间.所有的驱动程序都是运行在内核空间,内核空间虽然很大,但总是有限的,而在这有限的空间中,其最后 ...

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

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

  7. Linux设备模型——设备驱动模型和sysfs文件系统解读

    本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...

  8. linux内核奇遇记之md源代码解读之四

    linux内核奇遇记之md源代码解读之四 转载请注明出处:http://blog.csdn.net/liumangxiong 运行阵列意味着阵列经历从无到有,建立了作为一个raid应有的属性(如同步重 ...

  9. linux ad7606 驱动解读

    本文记录阅读linux ad7606驱动的笔记. 主要文件 drivers/staging/iio/adc/ad7606_spi.c drivers/staging/iio/adc/ad7606_co ...

随机推荐

  1. scikit-learn: isotonic regression(保序回归,非常有意思,仅做知识点了解,但差点儿没用到过)

    http://scikit-learn.org/stable/auto_examples/plot_isotonic_regression.html#example-plot-isotonic-reg ...

  2. Android怎样保证一个线程最多仅仅能有一个Looper?

    1. 怎样创建Looper? Looper的构造方法为private,所以不能直接使用其构造方法创建. private Looper(boolean quitAllowed) { mQueue = n ...

  3. Java 定时任务之Quartz

    使用CronTrigger CronTrigger 能够提供比 SimpleTrigger 更有具体实际意义的调度方案,调度规则基于 Cron 表达式,CronTrigger 支持日历相关的重复时间间 ...

  4. ios4--UIView的常见属性(尺寸和位置)

    // // ViewController.m // 08-UIView的常见属性(尺寸和位置) // // frame:相对于父控件左上角定位 // bounds:改变长宽,左上角是相对于自己 // ...

  5. C# Pen绘制虚线(System.Drawing.Pen与System.Windows.Media.Pen)

    一.绘制虚线的方法 GDI绘制,使用的是System.Drawing.Pen Pen pen = new Pen(Color.Red, 1);            pen.DashStyle = S ...

  6. [Swift通天遁地]三、手势与图表-(4)3DTouch功能在项目中的应用

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. [Swift通天遁地]六、智能布局-(5)给视图添加Align(对齐)和Fill(填充的约束以及Label的约束

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  8. Elasticsearch索引的操作,利用kibana(如何创建/删除一个es的索引?)

    我们已经通过索引一篇文档创建了一个新的索引 .这个索引采用的是默认的配置,新的字段通过动态映射的方式被添加到类型映射.现在我们需要对这个建立索引的过程做更多的控制:我们想要确保这个索引有数量适中的主分 ...

  9. HTML--使用mailto在网页中链接Email地址

    <a>标签还有一个作用是可以链接Email地址,使用mailto能让访问者便捷向网站管理者发送电子邮件.我们还可以利用mailto做许多其它事情.下面一一进行讲解,请看详细图示: 注意:如 ...

  10. python爬虫值requests模块

    - 基于如下5点展开requests模块的学习 什么是requests模块 requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求.功能强大,用法简洁高效.在 ...