多语言展示
当前在线:1219今日阅读:27今日分享:41

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

有时候我们在修改内核的时候会出现很多意想不到的错误,有的错误让你抓不着头脑,搞不懂为什么会出现这种错误,所以我们需要一些内核的错误函数来提示,这样我们才方便找到错误,本篇文章大概列举了一些常见的错误函数,仅供参考。
工具/原料
1

linux系统

2

linux内核编译

方法/步骤
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()宏就发挥作用了。先看源代码,再讲原理,看看内核中的巧妙设计思路。 /* include/linux/err.h */ 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 /* No record locks available */ #define ENOSYS 78 /* Function not implemented */ #define ENOMSG 80 /* No message of desired type */ #define EIDRM 81 /* Identifier removed */ #define ENOSR 82 /* Out of streams resources */ #define ETIME 83 /* Timer expired */ #define EBADMSG 84 /* Not a data message */ #define EPROTO 85 /* Protocol error */ #define ENODATA 86 /* No data available */ #define ENOSTR 87 /* Device not a stream */. ........ 现在应该知道为什么我写返回错误码的时候也加个负号如 -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) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量error 的值来决定要输出的字符串。   在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用'某些'函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。例子:#include 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

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的定义如下:/*   Standard streams. */   extern struct _IO_FILE *stdin; /* Standard input stream. */   extern struct _IO_FILE *stdout; /* Standard output stream. */   extern struct _IO_FILE *stderr; /* Standard error output stream. */ 在使用fprintf()函数时,通常我们可以将第一个参数设为stdout或者stderr,打印出错调试信息的时候则推荐使用stderr而不是 stdout,这是一种惯例,同时也由于内核在处理stdout和stderr时的优先级不一样,后者的优先级要高一些,因此有时候如果程序异常退出 时,stderr能得到输出,而stdout就不行。 printf(...) 实际上相当于fprintf(stdout, ...),这也是为什么我们不推荐使用它的原因。在输出调试信息的时候,我们推荐使用fprintf(stderr, …),或者使用某个指定的文件流fprintf(some_stream, …)。例子:#include 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.txtthis is first!this is third!文件tmp.txt中的内容为:this is second!PS:关于为什么 './fprint 2>tmp.txt' 中的 2 ;在UNIX系统中,标准输入、标准输出、标准错误输出分别被定义为0、1、2。

推荐信息