Linux 错误处理(字符设备基础三)

  在Linux字符设备驱动中,即使是最简单的注册字符设备,也存在注册失败的可能性,因此在之前编写的驱动代码中采用检查函数返回值的方式,确认函数是否成功执行

一、goto 语句

  在编写驱动程序时,驱动程序应该提供函数执行失败后处理的能力。如果驱动程序中函数执行失败了,必须取消掉所有失败前的注册,否则内核会处于一个不稳定的状态,因为它包含了不存在代码的内部指针。在处理 Linux 错误时,最好使用goto 语句,goto 语句的使用示例如下所示:

int init my_init_function(void)
{
	int err;
	err = register_this(ptr1, "skull");
	if (err)
		goto fail_this;
	err = register_that(ptr2, "skull");
	if (err)
		goto fail_that;
	err = register_those(ptr3, "skull");
	if (err)
		goto fail_those;

	return 0;
	
	fail_those:
		unregister_that(ptr2, "skull");
	fail_that:
		unregister_this(ptr1, "skull");
	fail_this:
		return err;
}

  在以上代码中试图注册 3 个虚构设备,goto 语句在失败情况下使用,对之前已经成功注册的设施进行注销。使用 goto 语句处理的时候,应该遵循“先进后出”的原则。

二、IS_ERR()

  对于任何一个指针来说,必然存在三种情况,一种是合法指针,一种是NULL(也就是空指针),一种是错误指针(也就是无效指针)。在 Linux 内核中,所谓的错误指针已经指向了内核空间的最后一页,例如,对于一个 64 位系统来说,内核空间最后地址为0xffffffffffffffff,那么最后一页的地址是 0xfffffffffffff000~0xffffffffffffffff,这段地址是被保留的,如果指针落在这段地址之内,说明是错误的无效指针。
  在 Linux 内核源码中实现了指针错误的处理机制,相关的函数接口主要有IS_ERR()、PTR_ERR()、ERR_PTR()等,其函数的源码在 include/linux/err.h 文件中,如下所示:

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

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

  如上所示,在 Linux 源码中 IS_ERR()函数其实就是判断指针是否出错,如果指针指向了内核空间的最后一页,就说明指针是一个无效指针,如果指针并不是落在内核空间的最后一页,就说明这指针是有效的。无效的指针能表示成一种负数的错误码,如果想知道这个指针是哪个错误码,使用 PTR_ERR 函数转化。0xfffffffffffff000~0xffffffffffffffff 这段地址和Linux错误码是一一对应的,内核错误码保存在 kernel\include\uapi\asm-generic\errno-base.h文件内

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_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	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#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 */

#endif

  对于IS_ERR()的使用,实例代码如下所示:

myclass = class_create(THIS_MODULE, "myclass");
if (IS_ERR(myclass)) 
{
    ret = PTR_ERR(myclass);
    goto fail;
}
mydevice = device_create(myclass, NULL, MKDEV(major, 0), NULL, "simple-device");
if (IS_ERR(mydevice)) 
{
    class_destroy(myclass);
    ret = PTR_ERR(mydevice);
    goto fail;
}

  在上述代码中,调用了 class_create()和 device_create()函数,必须使用IS_ERR()函数判断返回的指针是否是有效的,如果是无效的,需要调用 PTR_ERR()函数将无效指针转换为错误码,并进行错误码的返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值