如何理解Linux内核IS_ERR、ERR_PTR、PTR_ERR

文章目录

一、如何理解一些特殊的符号

二、内核空间的指针类型 

三、如何理解内核空间最大的错误码

四、解读内核相关内联函数的含义

五、附上内核源码(include/linux/err.h)


一、如何理解一些特殊的符号

1)inline:内联函数。内联函数的代码会直接嵌入到调用它的位置,调用几次就复制几次。

2)__must_check:调用函数时一定要处理函数的返回值,否则编译器会给出警告。

3)unlikely:告诉编译器括号内的值发生的概率很低,编译器就根据这个提示信息去做一些分支预测的编译优化。

4)(unsigned long)-MAX_ERRNO:用补码的方式表示-4095,32位系统为0xfffff001。

二、内核空间的指针类型 

1)有效指针;
2)NULL,空指针;
3)错误指针,或者说无效指针。

三、如何理解内核空间最大的错误码

1. 对于32位的系统来说,内核空间占用 3~4G(0xc0000000~0xffffffff)的虚拟地址。其中Linux采用了分页机制来管理内存,而CPU访问的是线性地址需要通过页表来转化成物理地址。所以,内核就约定了留出最后一个page(假设4k一个page)0xfffff000~0xffffffff,专门用来记录内核空间的错误指针。

2. 在 <linux/err.h> 中定义了 #define MAX_ERRNO  4095 最大错误码为 4095。至于这里为什么是 4095,而不是其它值?
我们知道负数在计算机中以二进制补码形式存储。因此,(unsigned long)-MAX_ERRNO 的值为0xfffff001,也就是大于等于 0xfffff001 的指针为非法指针。

注意 (unsigned long)-MAX_ERRNO 并不是 (unsigned long)减去MAX_ERRNO,而是对 -MAX_ERRNO 进行强制类型转化,其应该等价于  (unsigned long)(-MAX_ERRNO),-4095转换为无符号long型是0xfffff001,即:

#define IS_ERR_VALUE(x) unlikely((x) >= 0xfffff001)

四、解读内核相关内联函数的含义

1)IS_ERR() 调用了 IS_ERR_VALUE(),并将指针强转为 unsigned long 类型的错误码:IS_ERR() 判断指针是否非法,如果指针 < 0xfffff001 是有效的;如果指针 >= 0xfffff001 是无效的。即在 (0xfffff001,0xffffffff) 之间的就是错误指针。因此,可以用IS_ERR()来判断内核的指针是否有效。所以在内核中能经常看到返回负数的错误码如:-ENODEV。 参考内核对该函数的使用:

static struct sp_node *sp_alloc(unsigned long start, unsigned long end,
				struct mempolicy *pol)
{
	struct sp_node *n;
	struct mempolicy *newpol;

	n = kmem_cache_alloc(sn_cache, GFP_KERNEL);
	if (!n)
		return NULL;

	newpol = mpol_dup(pol);
	if (IS_ERR(newpol)) {
		kmem_cache_free(sn_cache, n);
		return NULL;
	}
	newpol->flags |= MPOL_F_SHARED;
	sp_node_init(n, start, end, newpol);

	return n;
}

2)ERR_PTR() 将一个错误码强转为一个错误指针,并作为函数的返回值使用。 参考内核对该函数的使用:

struct net_device *tc515_probe(int unit)
{
	struct net_device *dev = corkscrew_scan(unit);
	static int printed;

	if (!dev)
		return ERR_PTR(-ENODEV);

	if (corkscrew_debug > 0 && !printed) {
		printed = 1;
		pr_debug("%s", version);
	}

	return dev;
}

3)PTR_ERR() 将一个错误指针强转为一个错误码,并作为函数的返回值使用。 参考内核对该函数的使用:

static int vma_replace_policy(struct vm_area_struct *vma,
						struct mempolicy *pol)
{
	int err;
	struct mempolicy *old;
	struct mempolicy *new;

	pr_debug("vma %lx-%lx/%lx vm_ops %p vm_file %p set_policy %p\n",
		 vma->vm_start, vma->vm_end, vma->vm_pgoff,
		 vma->vm_ops, vma->vm_file,
		 vma->vm_ops ? vma->vm_ops->set_policy : NULL);

	new = mpol_dup(pol);
	if (IS_ERR(new))
		return PTR_ERR(new);

	if (vma->vm_ops && vma->vm_ops->set_policy) {
		err = vma->vm_ops->set_policy(vma, new);
		if (err)
			goto err_out;
	}

	old = vma->vm_policy;
	vma->vm_policy = new; /* protected by mmap_sem */
	mpol_put(old);

	return 0;
 err_out:
	mpol_put(new);
	return err;
}

五、附上内核源码(include/linux/err.h)

#include <linux/compiler.h>

#include <asm/errno.h>

/*
 * Kernel pointers have redundant information, so we can use a
 * scheme where we can return either an error code or a dentry
 * pointer with the same return value.
 *
 * This should be a per-architecture thing, to allow different
 * error and pointer decisions.
 */
#define MAX_ERRNO	4095

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

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;
}

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

static inline long __must_check IS_ERR_OR_NULL(const void *ptr)
{
	return !ptr || IS_ERR_VALUE((unsigned long)ptr);
}

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
基于DOS的多任务系统的实现 #include<stdio.h> #include<stdlib.h> #include <time.h> #include<dos.h> /*定义TCB状态*/ #define FINISHED 0 #define RUNNING 1 #define READY 2 #define BLOCKED 3 #define NTCB 5 /*定义最大空闲TCB个数*/ #define TC 2 /*定义时间片长度*/ #define NBUF 6 /*定义缓冲区大小*/ #define GET_INDOS 0x34 #define GET_CRIT_ERR 0x5d06 struct TCB{ unsigned char *stack; /*线程堆栈起始地址*/ unsigned ss; /*堆栈段址*/ unsigned sp; /*堆栈指针*/ char state; /*线程状态*/ char name[15]; /*线程外部标识符*/ struct TCB *next; }tcb[NTCB]; /*初始化堆栈时使用*/ struct int_regs{ unsigned bp, di, si, ds, es, dx, cx, bx, ax, ip, cs, flags, off, seg; }; char fpub[3]; int current= -1; /*定义一个当前TCB下标的全局变量*/ int timecount = 0; /*定义时间片*/ char far *indos_ptr = 0; /*该指针变量存放INDOS表示的地址*/ char far *crit_err_ptr = 0; /*该指针变量存放严重错误标志的地址*/ void interrupt (*old_int8)(void);/*旧的时钟中断处理程序*/ typedef int (far *codeptr)(void); /*定义了一个函数指针类型*/ int buffer[NBUF] = {-1,-1,-1,-1,-1,-1}; /*生产者消费者中的缓冲区*/ int in = 0; int out = 0; typedef struct{ int value; struct TCB *wq; }semaphore; semaphore mutex={1,NULL},empty={NBUF,NULL},full={0,NULL},Mutex2={1,NULL}; /*生产者消费者中的信号量*/ void InitDos(void) { union REGS regs; struct SREGS segregs; regs.h.ah = GET_INDOS; intdosx(&regs,&regs,&segregs); indos_ptr = MK_FP(segregs.es,regs.x.bx); if(_osmajor<3) crit_err_ptr = indos_ptr+1; else if(_osmajor==3 && _osminor==0) crit_err_ptr = indos_ptr-1; else{ regs.x.ax = GET_CRIT_ERR; intdosx(&regs,&regs,&segregs); crit_err_ptr = MK_FP(segregs.ds,regs.x.si); } } int DosBusy(void) { if(indos_ptr && crit_err_ptr) return (*indos_ptr || *crit_err_ptr); else return -1; } int finished(void) { int i; for(i=1; i<NTCB; i++) if(tcb[i].state != FINISHED) return 0; return 1; } int Seeknext() { int i,ks; i=current+1; ks=0; disable(); while(ks<NTCB) { if(tcb[i].state==READY) { return i; } i++; ks++; i=i%NTCB; } printf("Seeknext tcb is error!!\n"); enable(); } void interrupt my_swtch(void) { disable();/*开中断*/ /*保护正在执行的线程current的现场,暂停它的执行*/ tcb[current].ss=_SS; tcb[current].sp=_SP; if(tcb[current].state==RUNNING) tcb[current].state=READY; /*找到新的就绪线程i*/ current=Seeknext(); tcb[current].state=RUNNING; /*切换堆栈,恢复线程i的现场,把CPU分派给它*/ _SS=tcb[current].ss; _SP=tcb[current].sp; /*重新开始计时*/ timecount=0; enable();/*关中断*/ } void interrupt new_int8(void) { /*调用原来的时钟中断服务程序*/ (*old_int8)(); /*进行计时*/ timecount++; /*当前线程的时间片到否*/ if(timecount >= TC) if(!DosBusy()) { /*调用my_swtch进行重新调度*/ my_swtch(); } } void Destroy(int i) { if(tcb[i].state==RUNNING) { disable(); tcb[i].state=FINISHED; free(tcb[i].stack); enable(); } return; } void over(void) { Destroy(current); printf("%s is finished!\n", tcb[current].name); my_swtch(); } void tcb_state() { int i; printf("These are the information of all threads: \n"); for(i=0;i<NTCB-1;i++) { printf("the key word of the thread is : %d\n the name of thread is : %s\n the state of the thread is ",i,tcb[i].name); switch(tcb[i].state) { case (1): printf("Running \n");break; case (2): printf("Ready \n");break; case (3): printf("Blocked\n");break; case (0): printf("Finished\n");break; } } } /*创建线程*/ int create(char *name, codeptr code, int stacklen) { int i = 0; struct int_regs *temp; while(tcb[i].state != FINISHED) /*查找空闲TCB块*/ i++; if(i == NTCB) return -1; /*没有空闲TCB块可用*/ strcpy(tcb[i].name, name); tcb[i].state = READY; tcb[i].stack = (unsigned char*)malloc(stacklen); /*为新线程分配私有堆栈空间*/ tcb[i].stack = tcb[i].stack + stacklen; /*初始化新线程的私有堆栈*/ temp = (struct int_regs *)tcb[i].stack-1; temp->ds = _DS; temp->es = _ES; temp->flags = 0x200; temp->ip = FP_OFF(code); temp->cs = FP_SEG(code); temp->off = FP_OFF(over); /*将over函数压入堆栈,线程结束自动执行*/ temp->seg = FP_SEG(over); tcb[i].ss = FP_SEG(temp); tcb[i].sp = FP_OFF(temp); return i; } void block(semaphore *sem) { struct TCB *p; disable(); tcb[current].state = BLOCKED; p = sem->wq; if(p == NULL){ sem->wq = &tcb[current]; } else{ while(p->next != NULL) p = p->next ; p->next = &tcb[current]; } tcb[current].next = NULL; my_swtch(); enable(); } void wakeup(semaphore *sem) { struct TCB *p; disable(); p = sem->wq; if(p != NULL){ p->state = READY; sem->wq = sem->wq->next; } enable(); } void wait(semaphore *sem) { disable(); sem->value = sem->value - 1; if(sem->value < 0) { printf("\nthe thread of %s is blocked\n",tcb[current].name); block(sem); } enable(); } void signal(semaphore *sem) { disable(); sem->value = sem->value + 1; if(sem->value <= 0) wakeup(sem); enable(); } void proceducer() { int i, j, k; for(i = 1; i <=13;i++){ wait(&empty); wait(&mutex); buffer[in] = i*i; printf("%s puts a number of %d in the buffer \n",tcb[current].name, buffer[in]); /*for(j=0; j<10000; j++) for(k=0; k<10000; k++);*/ in = (in +1) % NBUF; signal(&mutex); signal(&full); } } void consumer() { int i, j, k; for(i = 1; i<=7;i++){ wait(&full); wait(&mutex); printf("consumer gets the number of %d from the buffer\n", buffer[out]); buffer[out] = -1; /*for(j=0; j<1000; j++) for(k=0; k<10000; k++);*/ out = (out+1) % NBUF; signal(&mutex); signal(&empty); } } void f1() { int i,j,k; wait(&Mutex2); strcpy(fpub,"f1"); signal(&Mutex2); for(i=1;i<=22;i++) { wait(&Mutex2); printf("%s is running !\n",fpub); signal(&Mutex2); for(j=0;j<10000;j++) for(k=0;k<10000;k++); } } void f2() { int i,j,k; wait(&Mutex2); /*做放入数的操作*/ strcpy(fpub,"f2"); signal(&Mutex2); for(i=1;i<=33;i++) { wait(&Mutex2); printf("%s is running !\n",fpub); signal(&Mutex2); for(j=0;j<10000;j++) for(k=0;k<10000;k++); } } void InitTcb()/*TCB的初始化*/ { int i; for(i=0;i<NTCB;i++) { tcb[i].state=FINISHED; tcb[i].name[0]='\0'; tcb[i].next=NULL; } } int main(void) { InitDos(); InitTcb(); old_int8 = getvect(8); strcpy(tcb[0].name, "main"); tcb[0].state = RUNNING; current = 0 ; create("proceducerOne", (codeptr)proceducer, 1024); create("consumer", (codeptr)consumer, 1024); /*create("proceducerTwo", (codeptr)proceducer, 1024); create("f1",(codeptr)f1,1024); create("f2",(codeptr)f2,1024);*/ tcb_state(NTCB); setvect(8, new_int8); my_swtch(); while(!finished()) ; strcpy(tcb[0].name, "\0"); tcb[0].state = FINISHED; setvect(8, old_int8); printf("\n"); tcb_state(NTCB); printf("\nMulti_task system terminated.\n"); getchar(); return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

积步千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值