内核中各种出错函数的总结

转:http://blog.sina.com.cn/s/blog_79e6215701010k7x.html

1、许多的内核函数需要返回一个指针,但是函数的调用可能失败,一般我们处理这样的情形都是返回一个NULL指针,就像malloc或kmalloc在没有获得指定的空间申请时的返回值一样。但是有时我们想知道导致函数失败的原因,但是返回NULL就显得信息不够。因此有些函数返回一个实际的错误编码以便对引起错误的原因做一些处理。很多内核接口通过把错误值编码到一个指针值中来返回错误信息。当处理这样的函数时,判断是否成功调用就不能是简单的和NULL进行比较。为了方便使用这样的类型接口,2.6的内核在linux/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);
}

所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即 ptr 0xfff == 0
这样ptr的值不可能落在(0xfffff000,0xffffffff)之间, 
而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsigned long, 
正好在(0xfffff000,0xffffffff)之间。因此可以用 

(unsigned long)ptr (unsigned long)-1000L
来判断内核函数的返回值是一个有效的指针,还是一个出错代码。像struct class *cls class_create();这种语句,其中返回的指针值并不kmalloc一样这么简单,只判断是否为NULL就可以了,内核是返回其错误值。那么我怎么来判断它呢,总不能用if()来将每个错误例出来吧,这里我们的IS_ERR()宏就发挥作用了。先看源代码,再讲原理,看看内核中的巧妙设计思路。

static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来。
所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即
ptr 0xfff == 0
这样ptr的值不可能落在(0xfffff000,0xffffffff)之间,而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsigned long,正好在(0xfffff000,0xffffffff)之间。因此可以用
(unsigned long)ptr (unsigned long)-1000L
也就等效于(x) >= (unsigned long)-MAX_ERRNO
其中MAX_ERRNO 为4095
来判断内核函数的返回值是一个有效的指针,还是一个出错代码。
涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了 最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的 0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,如果超过这个地址,则肯定是错误的。

而我们的错误码的值在内存中定义都是这样的(include/linux/errno.h):
......
#define ENOLCK 77 
#define ENOSYS 78 
#define ENOMSG 80 
#define EIDRM 81 
#define ENOSR 82 
#define ETIME 83 
#define EBADMSG 84 
#define EPROTO 85 
#define ENODATA 86 
#define ENOSTR 87 .
........

现在应该知道为什么我写返回错误码的时候也加个负号如 -ENOSYS这样子了。
至于PTR_ERR(), ERR_PTR(),只是强制转换以下而已,源代码如下(include/linux/err.h) :
static inline void __must_check ERR_PTR(long error)
{
return (void *) error;
}
static inline long __must_check PTR_ERR(const void *ptr)
{
return (long) ptr;
}
所以像上面的cls例子可以这样写:
struct class *cls class_create(....);
if(IS_ERR(cls))
{
ret =PTR_ERR(cls);
return ret;
}
2、void perror(const char *s);

perror )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量error 的值来决定要输出的字符串。   

在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。

例子:#include <stdio.h> 

int main(void)   

  FILE *fp   

fp fopen( "/root/noexitfile", "r+" );   

if NULL == fp   

  

perror("/root/noexitfile");   

  

return 0;   

}

输出结果:/root/noexitfile: No such file or directory

3、fprintf函数也可以打印错误的信息!见下面解析(摘自网上的一些好文章,并做了一些修改):

int fprintf(FILE *restrict fp, const char *restrict format, ...);

所谓流,通常是指程序输入或输出的一个连续的字节序列,设备(例如鼠标、键 盘、磁盘、屏幕、调制解调器和打印机)的输入和输出都是用流来处理的,在C语言中,所有的流均以文件的形式出现——不一定是物理磁盘文件,还可以是对应于某个输入/输出源的逻辑文件。C语言提供了5种标准的流,你的程序在任何时候都可以使用它们,并且不必打开或关闭它们。以下列出了这5种标准的流。

名称

描述

例子

stdin 
stdout
stderr 
stdprn 
stdaux

标准输入
标准输出
标准错误
标准打印机
标准串行设备

键盘
屏幕
屏幕
LPT1端口
COM1端口

其中,stdprn和stdaux并不总是预先定义好的,因为LPT1和COM1端口在某些操作系统中是没有意义的,而stdin,stdout 和stderr总是预先定义好的。此外,stdin并不一定来自键盘,stdout也并不一定显示在屏幕上,它们都可以重定向到磁盘文件或其它设备上。我们在头文件stdio.h中可以找到stdin,stdout 和stderr的定义如下:


extern struct _IO_FILE *stdin; 
extern struct _IO_FILE *stdout; 
extern struct _IO_FILE *stderr; 

 

在使用fprintf()函数时,通常我们可以将第一个参数设为stdout或者stderr,打印出错调试信息的时候则推荐使用stderr而不是 stdout,这是一种惯例,同时也由于内核在处理stdout和stderr时的优先级不一样,后者的优先级要高一些,因此有时候如果程序异常退出 时,stderr能得到输出,而stdout就不行。 
printf(...) 实际上相当于fprintf(stdout, ...),这也是为什么我们不推荐使用它的原因。在输出调试信息的时候,我们推荐使用fprintf(stderr, …),或者使用某个指定的文件流fprintf(some_stream, …)。

例子:#include<stdio.h>
void main()
{
    fprintf(stdout,"this is first!\n");
    fprintf(stderr,"this is second!\n");
    printf("this is third!\n");
}

stdout标准输出、stderr标准错误输出,二者默认向屏幕输出。

    如果重定向输出到磁盘文件,则stdout输出到该文件,而stderr仍输出到屏幕。

stderr是作为程序运行中的错误显示出来的,若要把它重定向到磁盘文件,需要运行如下命令:

#./fprint 2>tmp.txt

this is first!

this is third!

文件tmp.txt中的内容为:

this is second!

PS:关于为什么 "./fprint 2>tmp.txt" 中的 ;在UNIX系统中,标准输入、标准输出、标准错误输出分别被定义为0、1、2。

附录:各种格式符:





 

目前就这几种错误提示函数,以后若有再添加进去!


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值