C语言没有错误处理机制,所以一旦程序某个部分出错,需要退出的时候,就需要先把之前分配的资源先释放掉再退出。
而释放的过程是很有讲究的。
一般我们遵循先申请的资源后释放,后申请的资源先释放的原则。和栈比较像。
举个例子
struct _c_tag { int *c; }; struct _b_tag { int b; struct _c_tag *c; }; struct _a_tag { int a; struct _b_tag *b; }; void func() { struct _a_tag* _a_value ; _a_value = malloc(sizeof(struct _a_tag)); ... _a_value->b = malloc(sizeof(struct _b_tag)); ... _a_value->b->c = malloc(sizeof(struct _c_tag)); ... free(_a_value->b->c); free(_a_value->b); free(_a_value); }
可以看到,如果先free掉_a_value就无法访问 b,c两个里面分配的资源的。
一般情况下我们是这样做的。
下面是我写的一个led驱动注册的例子,可以发现若是在程序的越后面出错,错误处理就会变得越冗余一些。
static int s5pv210_led_probe(struct platform_device *pdev) { /* 得到设备传过来来的数据 */ struct s5pv210_led_platdata *pdata = pdev->dev.platform_data; struct s5pv210_gpio_led *led = NULL; int ret = -1; printk(KERN_INFO"-----------s5pv210_led_probe-------------\n"); led = kzalloc(sizeof(struct s5pv210_gpio_led),GFP_KERNEL); if(NULL == led) { dev_err(&pdev->dev, "no memoey for device \n"); return -ENOMEM; } /* 使用gpiolib库,申请gpio */ if(gpio_request(pdata->gpio,pdata->name)) { printk(KERN_ERR"gpio_request fail\n"); kfree(led); return -EINVAL; } else { /* 初始化gpio为输出 */ gpio_direction_output(pdata->gpio,1); } /* 把动态申请到的driver data绑定到相应的设备的私有数据中 */ platform_set_drvdata(pdev, led); led->cdev.name = pdata->name; led->cdev.brightness_set = s5pv210_led_brightness_set; led->cdev.brightness = 0; // led->cdev.flags = pdata->flags; //这个标志必须用内核定义的,自己定义的可能会和内核的重复 led->pdata = pdata; /* 利用led设备类模型注册该设备 */ ret = led_classdev_register(&pdev->dev,&led->cdev ); if(ret) { dev_err(&pdev->dev, "led classdev_register fail\n"); kfree(led); gpio_free(pdata->gpio); return ret; } return 0; }
观看内核代码,发现内核的错误处理更简洁一些。
下面是我借用内核驱动错误处理机制实现的一个驱动注册函数。
//注册字符文件 static int __init chrdev_init(void) { int ret = -1; printk(KERN_INFO "chrdev_init\n"); //申请分配设备号 dev_num接收新分配的设备号,0次设备号起始,1次设备号个数,""设备名称 ret = alloc_chrdev_region(&dev_num, 0, 1, "test chrdev"); if(ret < 0) { printk(KERN_ERR"alloc_chrdev_region fail %s(%d)\n",__FILE__,__LINE__); goto err_alloc_chrdev_region; } printk(KERN_INFO"alloc_chrdev_region success... major = %d,minor = %d \n",MAJOR(dev_num),MINOR(dev_num)); //初始化设备fops到cdev cdev_init(&test_cdev, &test_fops); //设备操作信息注册到内核中去 ret = cdev_add(&test_cdev, dev_num, 1); if(ret) { printk(KERN_ERR"cdev_add fail %s(%d)\n",__FILE__,__LINE__); goto err_cdev_add; } //动态请求内存使用 if(!request_mem_region(GPJ0CON_PA_ADDR, 8, "led dynamic map")) { printk(KERN_ERR"request_mem_region fail %s(%d)\n",__FILE__,__LINE__); goto err_request_mem_region; } //得到物理地址对应的虚拟地址 pGPJ0CON = ioremap(GPJ0CON_PA_ADDR, 8); if(!pGPJ0CON) { printk(KERN_ERR"ioremap fail %s(%d)\n",__FILE__,__LINE__); goto err_ioremap; } pGPJ0DAT = pGPJ0CON + 1; return 0; err_ioremap: release_mem_region(GPJ0CON_PA_ADDR, 8); err_request_mem_region: cdev_del(&test_cdev); err_cdev_add: unregister_chrdev_region(dev_num, 1); err_alloc_chrdev_region: return -EINVAL; }
可以发现,虽然要处理的错误更多了,但不像上面的那样,在每个return 里面处理错误,而是归结到程序最后统一处理。使得整个错误处理不会显得很冗余。
这里使用goto语句也并不会显的代码很乱。