基本的错误码在内核中的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); }这样就可以提取错误码,然后根据错误码再做什么处理就由具体的驱动来处理了。我感觉其实将内核的机构与原理理解清楚了,内核的一些技巧就非常好理解了。