• 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      在以上代码中试图注册 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

      如上所示,在 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

      对于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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

  • 相关阅读:
    c++ 获取当前时间(精确至秒、毫秒和微妙)
    温故知新(十一)——IIC
    野生程序员的成长之路(续)--团队需要什么样的管理者?
    redis的c++ 客户端 redis-plus-plus
    我在风口 有事想聊——隐私计算
    17.电话号码的字母组合
    【HEC-RAS】模型不稳定故障排除技巧(一)
    两个妙招教你怎么拍照识别植物,增长见识
    23 Vue 项目初始化和gitee项目同步
    Sqoop数据导入操作
  • 原文地址:https://blog.csdn.net/xxxx123041/article/details/134019432