在linux内核源码中提供了一些判断返回指针是否错误的内联函数。其中主要有:PTR_ERR、ERR_PTR、IS_ERR、IS_ERR_OR_NULL。
在linux内核中有些函数是返回指针的,如kmalloc分配内存,如果分配不到内存就会返回NULL指针。因此我们可以通过判断是否是NULL指针来判断kmalloc是否执行成功。但是存在有些函数返回错误时,我们不仅需要知道函数错了,还需要知道错在哪里。也就是说我们需要获得错误码。
内核中没有这个变量,因此内核开发者根据内核的地址空间的特点用了一种新的方法来获得错误码。那就是PTR_ERR,ERR_PTR,IS_ERR,IS_ERR_OR_NULL这四个内联函数。
错误码与内核地址空间的相关特点:
基本的错误码在内核中的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。
内核空间也就是内核的地址空间。我们知道Linux是基于虚拟内存的内核,所以CPU访问的是线性地址,而线性地址需要通过页表来转化为物理地址,若一个线性地址的页表不存在的话会发生缺页异常。Linux在内核地址空间映射了大于0xc0000000的所有可用线性地址,而对于小于0xc0000000的线性地址在内核态是没有页表的,内核也从不使用小于0xc0000000的线性地址。也就是说内核返回指针的函数,如果执行正确,返回的指针的大小绝对不会小于0xc0000000。如果小于那么肯定执行错误。
可以利用这一点。内核函数都遵守一个约定,如果不能返回正确的指针,那么就返回错误的,我们把返回的错误指针作为错误码。因为错误码都是整数,而返回的是指针,所以需要强制转换一下,这就诞生了这四个宏PTR_ERR,ERR_PTR,IS_ERR,IS_ERR_OR_NULL。这四个宏(内联函数)的定义在err.h中。这些内联函数主要在linux内核源码如下include/linux/err.h。
//include/linux/err.h
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);
}
判断是否为错误指针也是很简单unlikely((x) >= (unsigned long)-MAX_ERRNO),错误码在返回的时候都去负数了,负数大的他的绝对值就小了。