Linux内核中错误码与错误处理函数

Linux错误处理

1. Linux下编号前50错误码

内核定义了许多常见的错误码,如EPERM(操作不允许)、ENOENT(无此文件或目录)、EINTR系统调用被中断)等。
位于文件linux\include\uapi\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 */
#define	EDEADLK		  35	/* Resource deadlock would occur */
#define	ENAMETOOLONG  36	/* File name too long */
#define	ENOLCK		  37	/* No record locks available */
#define	ENOSYS		  38	/* Invalid system call number */

#define	ENOTEMPTY	  39	/* Directory not empty */
#define	ELOOP		  40	/* Too many symbolic links encountered */
#define	EWOULDBLOCK	 EAGAIN	/* Operation would block */
#define	ENOMSG		  42	/* No message of desired type */
#define	EIDRM		  43	/* Identifier removed */
#define	ECHRNG		  44	/* Channel number out of range */
#define	EL2NSYNC	  45	/* Level 2 not synchronized */
#define	EL3HLT		  46	/* Level 3 halted */
#define	EL3RST		  47	/* Level 3 reset */
#define	ELNRNG		  48	/* Link number out of range */
#define	EUNATCH		  49	/* Protocol driver not attached */
#define	ENOCSI		  50	/* No CSI structure available */
.......................................
...... ...............................

2. linux错误处理函数

Linux 下错误处理函数位于 linux\include\linux\err.h

/* SPDX-License-Identifier: GPL-2.0 */
/* 声明遵循GPL-2.0许可证 */

#ifndef _LINUX_ERR_H
#define _LINUX_ERR_H

/* 包含必要的编译器和类型定义的头文件 */
#include <linux/compiler.h>
#include <linux/types.h>

/* 包含架构特定的errno定义 */
#include <asm/errno.h>

/*
 * 内核指针包含冗余信息,因此我们可以使用一种方案,通过该方案可以返回错误码或普通指针,
 * 并且返回值相同。这应该是按架构划分的事情,以允许不同的错误和指针决策。
 */
#define MAX_ERRNO	4095
/* 定义最大errno值,用于确定错误指针的范围 */

#ifndef __ASSEMBLY__
/* 如果不是汇编代码,则定义以下宏和函数 */

/* 检查给定的值是否是一个错误指针的值 */
#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

/*
 * 将一个错误码转换为一个错误指针。
 * 注意:这里直接将错误码(一个long类型)转换为void*类型,
 * 利用了错误码通常为负数的特性,以及void*指针无法直接表示负地址的特性。
 */
static inline void * __must_check ERR_PTR(long error)
{
	return (void *) error;
}

/*
 * 将一个错误指针转换回其对应的错误码。
 * 注意:这里直接将void*类型的错误指针转换回long类型。
 */
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);
}

/* 检查给定的指针是否为NULL或错误指针 */
static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
{
	return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr);
}

/**
 * ERR_CAST - 显式地将一个错误值指针转换为另一种指针类型
 * @ptr: 要转换的指针。
 *
 * 显式地将一个错误值指针转换为另一种指针类型,以便清楚地表明正在执行的操作。
 * 注意:这个宏实际上并没有进行特殊的转换,只是简单地返回了输入指针。
 * 它主要用于在代码中清晰地表明正在进行的操作是类型转换。
 */
static inline void * __must_check ERR_CAST(__force const void *ptr)
{
	/* 去除const限定符 */
	return (void *) ptr;
}

/*
 * 如果给定的指针是一个错误指针,则返回其错误码;否则返回0。
 * 这在需要处理可能返回错误指针的函数调用时非常有用。
 */
static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
{
	if (IS_ERR(ptr))
		return PTR_ERR(ptr);
	else
		return 0;
}

/* 已弃用:建议使用PTR_ERR_OR_ZERO代替 */
#define PTR_RET(p) PTR_ERR_OR_ZERO(p)

#endif /* __ASSEMBLY__ */

#endif /* _LINUX_ERR_H */

这段代码是Linux内核中用于处理错误指针的宏和函数定义,它们被包含在linux/err.h头文件中。这个机制允许内核函数在返回指针时,如果发生错误,可以返回一个特殊的“错误指针”,这个指针实际上是一个错误码的负值(通过ERR_PTR宏创建)。调用者可以使用IS_ERRPTR_ERR等宏和函数来检查和处理这些错误指针。

下面是对这些宏和函数的解释:

  1. MAX_ERRNO:定义了最大错误码值,为4095。这是因为在Linux中,错误码通常是正整数,并且使用-errno来表示错误指针(通过取负值来避免与有效的内存地址冲突)。

  2. IS_ERR_VALUE(x):检查给定的值x是否是一个错误指针的值。它通过比较x是否大于等于-MAX_ERRNO来判断。

  3. ERR_PTR(error):将一个错误码error转换为一个错误指针。这个宏简单地将错误码(一个long类型的值)强制转换为void*类型。

  4. PTR_ERR(ptr):将一个错误指针ptr转换回它表示的错误码。这个宏通过强制转换ptrlong类型来实现。

  5. IS_ERR(ptr):检查给定的指针ptr是否是一个错误指针。它通过调用IS_ERR_VALUE宏来判断。

  6. IS_ERR_OR_NULL(ptr):检查给定的指针ptr是否为NULL或是一个错误指针。

  7. ERR_CAST(ptr):将给定的指针ptr(可能是一个错误指针)显式地转换为另一个指针类型,同时去除其const属性。这个宏主要用于类型安全的转换,但实际上它并没有改变指针的值或类型(除了去除const)。

  8. PTR_ERR_OR_ZERO(ptr):如果ptr是一个错误指针,则返回它表示的错误码;否则返回0。这个宏通常用于处理可能返回错误指针的函数调用,如果调用成功则返回0。

  9. PTR_RET(p):这是一个已废弃的宏,其功能与PTR_ERR_OR_ZERO相同。建议使用PTR_ERR_OR_ZERO代替PTR_RET

这个头文件和其中的宏、函数是Linux内核中处理错误指针的标准方式,它们使得内核代码更加健壮和易于维护。通过检查返回值是否为错误指针,调用者可以适当地处理错误情况,而不会导致未定义的行为或安全漏洞。

3 .举例

一般情况下,返回错误的方式: return -ERROR

例如 #define EPERM 1 /* Operation not permitted */
操作不允许 return -EPERM

实际案例

在Linux内核空间编写驱动程序时,处理“操作不允许”(EPERM)错误通常涉及检查调用者的权限,并在权限不足时返回适当的错误码。

一个字符设备驱动程序举例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define DEVICE_NAME "my_privileged_device"

static int major;
static struct class *my_class;
static struct cdev my_cdev;

static int my_open(struct inode *inode, struct file *file) {
    // 在这里检查权限,例如检查调用者是否是root用户
    if (!capable(CAP_SYS_ADMIN)) { // 检查是否具有系统管理员权限
        printk(KERN_WARNING "Operation not permitted for %s\n", current->comm);
        return -EPERM; // 返回EPERM错误码
    }

    // 如果权限检查通过,则执行其他打开逻辑(如果有的话)
    // ...

    return 0; // 返回0表示成功
}

static int my_release(struct inode *inode, struct file *file) {
    // 释放资源(如果有的话)
    // ...

    return 0; // 返回0表示成功
}

// 其他文件操作函数,如read、write等,可以根据需要实现
// ...

static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    // .read = my_read,
    // .write = my_write,
    // 其他文件操作可以按需添加
};

static int __init my_driver_init(void) {
    int ret;

    // 分配主设备号
    if ((major = register_chrdev(0, DEVICE_NAME, &my_fops)) < 0) {
        printk(KERN_ALERT "Failed to register character device\n");
        return major; // 返回错误码
    }

    // 创建类
    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(my_class)) {
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ALERT "Failed to create class\n");
        return PTR_ERR(my_class); // 返回错误码
    }

    // 创建设备节点
    device_create(my_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);

    // 初始化cdev结构并添加到内核
    cdev_init(&my_cdev, &my_fops);
    ret = cdev_add(&my_cdev, MKDEV(major, 0), 1);
    if (ret < 0) {
        device_destroy(my_class, MKDEV(major, 0));
        class_destroy(my_class);
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ALERT "Failed to add cdev\n");
        return ret; // 返回错误码
    }

    printk(KERN_INFO "Driver initialized successfully\n");
    return 0; // 返回0表示成功
}

static void __exit my_driver_exit(void) {
    // 清理资源
    cdev_del(&my_cdev);
    device_destroy(my_class, MKDEV(major, 0));
    class_destroy(my_class);
    unregister_chrdev(major, DEVICE_NAME);

    printk(KERN_INFO "Driver exited successfully\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux driver example with permission check");

在这个示例中,my_open函数检查调用者是否具有系统管理员权限(CAP_SYS_ADMIN)。如果没有,它将打印一条警告消息并返回-EPERM错误码。这表示调用者没有足够的权限来打开设备。

对返回错误码的处理举例

IS_ERR_VALUE, ERR_PTR, PTR_ERR, 和 IS_ERR 是一组用于错误处理的宏和函数。它们允许内核函数返回指针类型的值时,能够区分有效的指针地址和错误码(通常是通过负整数表示的)。

#include <linux/errno.h>
#include <linux/types.h>


// 假设这是一个可能返回错误的内核函数
void *my_function_that_might_fail(void) {
    // 模拟一个错误情况
    return ERR_PTR(-EPERM); // 返回EPERM错误
}

// 使用这些宏和函数来处理错误的示例函数
int process_my_function(void) {
    void *result = my_function_that_might_fail();

    if (IS_ERR(result)) {
        // 处理错误,将指针转成错误码
        long error = PTR_ERR(result);
        //打印错误码
        printk(KERN_ERR "my_function_that_might_fail failed with error: %ld\n", error);
        // 根据错误码执行相应的错误处理逻辑
        // ...
        return error; // 将错误码返回给调用者
    } else if (IS_ERR_OR_NULL(result)) {
        // 处理NULL指针的情况(虽然在这个例子中不太可能,因为ERR_PTR不会返回NULL)
        printk(KERN_ERR "my_function_that_might_fail returned NULL\n");
        // 执行相应的错误处理逻辑
        // ...
        return -EINVAL; // 或者其他适当的错误码
    } else {
        // 成功情况:处理有效的指针
        // ...
        printk(KERN_INFO "my_function_that_might_fail succeeded\n");
        // 执行成功逻辑
        // ...
        return 0; // 返回0表示成功
    }
}

在这个示例中,my_function_that_might_fail 函数模拟了一个错误情况,并返回了 ERR_PTR(-EPERM),表示操作不允许。调用者 process_my_function 使用 IS_ERR 宏来检查返回值是否是一个错误指针。如果是,它使用 PTR_ERR 宏将错误指针转换回错误码,并打印出错误信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/924080.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

排序算法之选择排序堆排序

算法时间复杂度辅助空间复杂度稳定性选择排序O(N^2)O(1)不稳定堆排序O(NlogN)O(1)不稳定 1.选择排序 这应该算是最简单的排序算法了&#xff0c;每次在右边无序区里选最小值&#xff0c;没有无序区时&#xff0c;就宣告排序完毕 比如有一个数组&#xff1a;[2,3,2,6,5,1,4]排…

电视网络机顶盒恢复出厂超级密码大全汇总

部分电视机顶盒在按遥控器设置键打开设置时&#xff0c;会弹出设置密码弹窗&#xff0c;需输入密码才能操作其中内容。 如下图所示&#xff1a; 部分电视机顶盒在选择恢复出厂设置时&#xff0c;会出现设置密码弹窗&#xff0c;只有输入操作密码后才能进行恢复出厂设置的操作。…

继续完善wsl相关内容:基础指令

文章目录 前言一、我们需要安装wsl,这也是安装docker desktop的前提,因此我们在这篇文章里做了介绍:二、虽然我们在以安装docker desktop为目的时,不需要安装wsl的分发(distribution),但是装一个分发也是有诸多好处的:三、在使用wsl时,不建议把东西直接放到系统里,因…

基于STM32的智能风扇控制系统

基于STM32的智能风扇控制系统 持续更新&#xff0c;欢迎关注!!! ** 基于STM32的智能风扇控制系统 ** 近几年&#xff0c;我国电风扇市场发展迅速&#xff0c;产品产出持续扩张&#xff0c;国家产业政策鼓励电风扇产业向高技术产品方向发展&#xff0c;国内企业新增投资项目投…

Zero to JupyterHub with Kubernetes中篇 - Kubernetes 常规使用记录

前言&#xff1a;纯个人记录使用。 搭建 Zero to JupyterHub with Kubernetes 上篇 - Kubernetes 离线二进制部署。搭建 Zero to JupyterHub with Kubernetes 中篇 - Kubernetes 常规使用记录。搭建 Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s。 参考&…

docker-compose搭建xxl-job、mysql

docker-compose搭建xxl-job、mysql 1、搭建docker以及docker-compose2、下载xxl-job需要数据库脚本3、创建文件夹以及docker-compose文件4、坑来了5、正确配置6、验证-运行成功 1、搭建docker以及docker-compose 略 2、下载xxl-job需要数据库脚本 下载地址&#xff1a;https…

HTTP有哪些风险?是怎么解决的?

一、风险 HTTP是通过明文传输的&#xff0c;存在窃听风险、篡改风险以及冒充风险。 二、如何解决 HTTPS在HTTP的下层加了一个SSL/TLS层&#xff0c;保证了安全&#xff0c;通过混合加密解决窃听风险、数字签名解决篡改风险、数字证书解决冒充风险。 &#xff08;1&#xff0…

《Django 5 By Example》阅读笔记:p339-p358

《Django 5 By Example》学习第13天&#xff0c;p359-p382总结&#xff0c;总计24页。 一、技术总结 1.session (1)session 存储方式 Database sessions File-based sessions Cached sessions Cached database sessions Cookie-based sessions (2)设置 CART_SESSION_I…

Python数据分析(OpenCV)

第一步通过pip安装依赖包&#xff0c;执行一下命令 pip install opencv-python 如果是Anaconda请在工具中自行下载 下载好咋们就可以在环境中使用了。 人脸识别的特征数据可以到 github上面下载&#xff0c;直接搜索OpenCV 然后我们在源码中通过cv2的级联分类器引入人脸的特征…

(免费送源码)计算机毕业设计原创定制:Java+ssm+JSP+Ajax SSM棕榈校园论坛的开发

摘要 随着计算机科学技术的高速发展,计算机成了人们日常生活的必需品&#xff0c;从而也带动了一系列与此相关产业&#xff0c;是人们的生活发生了翻天覆地的变化&#xff0c;而网络化的出现也在改变着人们传统的生活方式&#xff0c;包括工作&#xff0c;学习&#xff0c;社交…

工业AI质检 AI质检智能系统 尤劲恩(上海)信息科技有限公司

来的现代化工厂&#xff0c;将逐步被无人化车间取代&#xff0c;无人工厂除了产线自动化&#xff0c;其无人质检将是绕不开的话题。尤劲恩致力于帮助工业制造领域上下游工厂减员增效、提高品质效率&#xff0c;真正实现无人质检IQC/IPQC/OQC的在线质检系统。分析生产环节真实品…

C 语言数组与函数:核心要点深度剖析与高效编程秘籍

我的个人主页 我的专栏&#xff1a;C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 引言数组基础 2.1 数组的定义与初始化 2.2 一维数组的基本操作 2.3 二维数组及其应用 2.4 数组与指针的关系函数基础 3.1 函数的定义与调用 3.2…

XML JSON

XML 与 JSON 结构 XML&#xff08;eXtensible Markup Language&#xff09; 1. 定义 XML 是一种标记语言&#xff0c;用于描述数据的结构和内容。主要用于数据存储与交换。 2. 特点 可扩展性&#xff1a;用户可以自定义标签。层次化结构&#xff1a;数据以树形结构组织&…

蓝桥杯备赛笔记(一)

这里的笔记是关于蓝桥杯关键知识点的记录&#xff0c;有别于基础语法&#xff0c;很多内容只要求会用就行&#xff0c;无需深入掌握。 文章目录 前言一、编程基础1.1 C基础格式和版本选择1.2 输入输出cin和cout&#xff1a; 1.3 string以下是字符串的一些简介&#xff1a;字符串…

[代码随想录Day24打卡] 93.复原IP地址 78.子集 90.子集II

93.复原IP地址 一个合法的IP地址是什么样的&#xff1a; 有3个’.分割得到4个数&#xff0c;每个数第一个数不能是0&#xff0c;不能含有非法字符&#xff0c;不能大于255。 这个是否属于合法IP相当于一个分割问题&#xff0c;把一串字符串分割成4部分&#xff0c;分别判断每…

v-for产生 You may have an infinite update loop in a component render function

参考文章&#xff1a; 报错解析 [Vue warn]: You may have an infinite update loop in a component render function. 另外一个解决方法 例如: MyList 是一个数组&#xff0c;我希望将排序后的结果返回进行for循环&#xff0c;因此设计了一个myMethon函数 <div v-for"…

中国前首富胡志标亮相创客匠人盛会,点燃创始人 IP 新思维火花

创客匠人正式官宣&#xff01;原爱多VCD创始人、中国前首富胡志标受邀出席创客匠人5000人“全球创始人IP领袖高峰论坛”&#xff0c;将与我们携手共赴这场商业巅峰盛宴。 由创客匠人打造的“全球创始人IP领袖高峰论坛”将在2024年12月26日-28日在厦门市国际博览会议中心如期举…

qsort函数详解+代码展示

文章目录 概要系列文章目录前言(1) 定义(2) 使用&#xff08;举例子 上代码&#xff09;1、定义数组&#xff1a;2、定义比较函数&#xff1a;3、调用 qsort&#xff1a;4、输出结果&#xff1a; (3) 注意事项 小结 概要 本篇博客将详细地介绍qsort排序函数&#xff0c;&#x…

CSS之3D转换

三维坐标系 三维坐标系其实就是指立体空间&#xff0c;立体空间是由3个轴共同组成的。 x轴:水平向右注意:x右边是正值&#xff0c;左边是负值 y轴:垂直向下注意:y下面是正值&#xff0c;上面是负值 z轴:垂直屏幕注意:往外面是正值&#xff0c;往里面是负值 3D移动 translat…

2024年nvm保姆级安装教程

需求&#xff1a;当前我的nodejs的版本是6.14.10&#xff0c;想切换为更高的版本。故使用nvm工具来实现不同node版本之间的切换 目录 一、删除node二、nvm安装三、配置nvm镜像四、安装所需要的nodejs版本nvm常用命令 一、删除node 第一步&#xff1a;首先在控制面板删除node.j…