IS_ERR()说明

  感谢fudan_abc的分享,本篇文章选自他的《Linux那些事儿之我是Hub(3)一样的精灵不一样的API》     

        人的无聊,有时候很难用语言表达.以下关于IS_ERR的文字仅献给无聊的你.如果你对内存管理没有任何兴趣,就不用往下看了,跳到下一节吧.要想明白IS_ERR(),首先你得知道有一种空间叫做内核空间,不清楚也不要紧,我也不是很清楚,曾经,在复旦,上操作系统这门课的时候,我一度以为我已经成为天使了,因为我天天上课都在听天书.后来,确切地说是去年,我去微软全球技术中心(GSTC)面试的时候,那个manager就要我解释这个名词,要我谈一谈对内核空间和用户空间的理解,其实我也挺纳闷的,我只不过是希望能成为微软的一名技术支持工程师,居然还要懂内核,你说这是什么世道?中学时候,老师不是跟我说只要学好数理化,走遍天下都不怕吗?算了,不去想这些伤心往事了.结合IS_ERR()的代码来看,来自include/linux/err.h:

8 /*

9 * Kernel pointers have redundant information, so we can use a

10 * scheme where we can return either an error code or a dentry

11 * pointer with the same return value.

12 *

13 * This should be a per-architecture thing, to allow different

14 * error and pointer decisions.

15 */

16 #define MAX_ERRNO 4095

17

18 #ifndef __ASSEMBLY__

19

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

21

22 static inline void *ERR_PTR(long error)

23 {

24 return (void *) error;

25 }

26

27 static inline long PTR_ERR(const void *ptr)

28 {

29 return (long) ptr;

30 }

31

32 static inline long IS_ERR(const void *ptr)

33 {

34 return IS_ERR_VALUE((unsigned long)ptr);

35 }

36

37 #endif


关于内核空间,我只想说,所有的驱动程序都是运行在内核空间,内核空间虽然很大,但总是有限的.要知道即便是我们这个幅员辽阔的伟大祖国其空间也是有限的,也只有960万平方公里,所以内核空间当然也是一个有限的空间,而在这有限的空间中,其最后一个page是专门保留的,也就是说一般人不可能用到内核空间最后一个page的指针.换句话说,你在写设备驱动程序的过程中,涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,一般人不得越雷池半步,如果你发现你的一个指针指向这个范围中的某个地址,那么恭喜你,你的代码肯定出错了.

那么你是不是很好奇,好端端的内核空间干嘛要留出最后一个page?这不是缺心眼儿吗?明明自己有1000块钱,非得对自己说只能用900块.实在不好意思,你说错了,这里不仅不是浪费一个page,反而是充分利用资源,把一个东西当两个东西来用.

看见16行那个MAX_ERRNO了吗?一个宏,定义为4095,MAX_ERRNO就是最大错误号,Linux内核中,出错有多种可能,因为有许许多多种错误,就像一个人进监狱,可能是像迟志强那样,在事业如日中天的时候强奸女孩,可能是像张君大哥那样,因为抢劫长沙友谊商城后又抢农业银行,亦或者是马加爵大侠那样,受同学的气,最终让铁锤来说话.关于Linux内核中的错误,我们看一下include/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 */


最常见的几个是-EBUSY,-EINVAL,-ENODEV,-EPIPE,-EAGAIN,-ENOMEM,我相信不用说你写过代码调试过代码,只要你使用过Linux就有可能见过这几个错误,因为它们确实经常出现.这些是每个体系结构里都有的,另外各个体系结构也都定义了自己的一些错误代码.这些东西当然也都是宏,实际上对应的是一些数字,这个数字就叫做错误号.而对于Linux内核来说,不管任何体系结构,最多最多,错误号不会超过4095.而4095又正好是比4k小1,即4096减1.而我们知道一个page可能是4k,也可能是更多,比如8k,但至少它也是4k,所以留出一个page出来就可以让我们把内核空间的指针来记录错误了.什么意思呢?比如我们这里的IS_ERR(),它就是判断kthread_run()返回的指针是否有错,如果指针并不是指向最后一个page,那么没有问题,申请成功了,如果指针指向了最后一个page,那么说明实际上这不是一个有效的指针,这个指针里保存的实际上是一种错误代码.而通常很常用的方法就是先用IS_ERR()来判断是否是错误,然后如果是,那么就调用PTR_ERR()来返回这个错误代码.只不过咱们这里,没有调用PTR_ERR()而已,因为起决定作用的还是IS_ERR(),而PTR_ERR()只是返回错误代码,也就是提供一个信息给调用者,如果你只需要知道是否出错,而不在乎因为什么而出错,那你当然不用调用PTR_ERR()了,毕竟,男人,简单就好

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
/* * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "esp_check.h" #include "bsp_board.h" #include "nvs_flash.h" #include "nvs.h" #include "settings.h" static const char *TAG = "settings"; #define NAME_SPACE "sys_param" #define KEY "param" static sys_param_t g_sys_param = {0}; static const sys_param_t g_default_sys_param = { .need_hint = 1, .sr_lang = SR_LANG_EN, .volume = 70, // default volume is 70% }; static esp_err_t settings_check(sys_param_t *param) { esp_err_t ret; ESP_GOTO_ON_FALSE(param->sr_lang < SR_LANG_MAX, ESP_ERR_INVALID_ARG, reset, TAG, "language incorrect"); ESP_GOTO_ON_FALSE(param->volume <= 100, ESP_ERR_INVALID_ARG, reset, TAG, "volume incorrect"); return ret; reset: ESP_LOGW(TAG, "Set to default"); memcpy(&g_sys_param, &g_default_sys_param, sizeof(sys_param_t)); return ret; } esp_err_t settings_read_parameter_from_nvs(void) { nvs_handle_t my_handle = 0; esp_err_t ret = nvs_open(NAME_SPACE, NVS_READONLY, &my_handle); if (ESP_ERR_NVS_NOT_FOUND == ret) { ESP_LOGW(TAG, "Not found, Set to default"); memcpy(&g_sys_param, &g_default_sys_param, sizeof(sys_param_t)); settings_write_parameter_to_nvs(); return ESP_OK; } ESP_GOTO_ON_FALSE(ESP_OK == ret, ret, err, TAG, "nvs open failed (0x%x)", ret); size_t len = sizeof(sys_param_t); ret = nvs_get_blob(my_handle, KEY, &g_sys_param, &len); ESP_GOTO_ON_FALSE(ESP_OK == ret, ret, err, TAG, "can't read param"); nvs_close(my_handle); settings_check(&g_sys_param); return ret; err: if (my_handle) { nvs_close(my_handle); } return ret; } esp_err_t settings_write_parameter_to_nvs(void) { ESP_LOGI(TAG, "Saving settings"); settings_check(&g_sys_param); nvs_handle_t my_handle = {0}; esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &my_handle); if (err != ESP_OK) { ESP_LOGI(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); } else { err = nvs_set_blob(my_handle, KEY, &g_sys_param, sizeof(sys_param_t)); err |= nvs_commit(my_handle); nvs_close(my_handle); } return ESP_OK == err ? ESP_OK : ESP_FAIL; } sys_param_t *settings_get_parameter(void) { return &g_sys_param; }
07-14
这段代码是一个设置参数的示例代码。它使用 ESP-IDF(Espressif IoT Development Framework)和 NVS(Non-volatile Storage)来读取和保存系统参数。 首先,我们定义了一些常量和变量。其中,TAG 是用于在日志中标识相关的日志消息的字符串。NAME_SPACE 是用于在 NVS 中保存参数的命名空间。KEY 是参数在 NVS 中的键名。 然后,我们定义了一个 sys_param_t 结构体类型的全局变量 g_sys_param,用于存储系统参数。我们还定义了一个 g_default_sys_param 常量,用于存储默认的系统参数。 接下来,我们定义了一个 settings_check 函数,用于检查系统参数的合法性。在函数中,我们检查了语言类型和音量值是否在有效范围内。如果参数不合法,则将系统参数重置为默认值。 然后,我们定义了 settings_read_parameter_from_nvs 函数,用于从 NVS 中读取系统参数。首先,我们打开 NVS,并检查是否找到了保存的参数。如果没有找到,则将系统参数设置为默认值,并调用 settings_write_parameter_to_nvs 函数将默认值保存到 NVS 中。如果找到了保存的参数,则从 NVS 中读取参数,并进行合法性检查。 接下来,我们定义了 settings_write_parameter_to_nvs 函数,用于将系统参数保存到 NVS 中。首先,我们调用 settings_check 函数对系统参数进行合法性检查。然后,我们打开 NVS,并将系统参数写入 NVS 中。 最后,我们定义了 settings_get_parameter 函数,用于获取当前的系统参数。 以上是一个简单的示例代码,用于读取和保存系统参数到 NVS 中。具体的使用方式和调用逻辑可能根据实际需求而有所不同。如果你需要更详细的说明,请告诉我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值