inotify监控时为什么出现了IN_IGNORED事件

inotify针对vim、gedit类的文件监控 想弄个监控文件是否被修改的子程序,在windows下,我是用的WIN32_FIND_DATA和FindFirstFile 这两个API,到了LINUX下面,准备转成inotify,结果在这搞笑了~~ 关于inotify的机制,很多人都讲过,我就不多说了。我这出现的问题是:      对文件是否被修改进行判断,需要用到inotify的IN_MODIFY文件系统事件。然而,我用gedit对 阅读详情

inotify还是不错的,玩着似乎很简单,但是坑也不少,如果不仔细查看官方文档,可能就真的不知道哪里存在坑,哪里需要注意。前段时间,在项目中使用inotify监控配置文件,以达到实时感知配置改变的目的。但近日查看线上日志发现,配置文件改变后,inotify并没有通知,结果导致配置一直未被更改。

    在描述之前,要说明一下,我代码中的inotify使用方式,这个方式和网上大多方式一样:

#include <errno.h>  
#include <stdio.h>  
#include <sys/inotify.h>  
  
static const char kszConfigPath[] = "/usr/local/path/to/config";  
static int s_running = 0;  
  
extern int reparse_config();  
  
int inotify_loop()  
{  
    int inot_fd = -1;  
    int watch_fd = -1;  
    unsigned int watch_flag = IN_MODIFY;  
    fd_set read_fds;  
    struct timeval seltime;  
    char buffer[16384];  
    int buffer_i = 0;  
  
    int inot_fd = inotify_init();  
    if (inot_fd < 0) {  
        printf("inotify_init error %d\n", errno);  
        return -1;  
    }  
  
    /* Watch config file. */  
    watch_fd = inotify_add_watch(inot_fd, kszConfigPath, watch_flag);  
    if (watch_fd < 0) {  
        printf("inotify_init error %d\n", errno);  
        close(inot_fd);  
        return -1;  
    }  
  
    while (s_running) {  
        int selret = 0;  
        int read_cnt = 0;  
  
        FD_ZERO(&read_fds);  
        FD_SET(inot_fd, &read_fds);  
        seltime.tv_sec = 1;  
        seltime.tv_usec = 0;  
  
        selret = select(inot_fd + 1, &read_fds, NULL, NULL, &seltime);  
        if (selret < 0) {  
            printf("select error %d\n", errno);  
            continue;  
        } else if (selret == 0) {  
            continue;  
        }else if (!FD_ISSET(inot_fd, &read_fds)) {  
            printf("inot_fd not in fdset\n");  
            continue;  
        }  
  
        read_cnt = read(fd, buffer, sizeof(buffer));  
        if (read_cnt <= 0) {  
            printf("read <= 0 (%d)\n", read_cnt);  
            continue;  
        }  
  
        buffer_i = 0;  
        while (buffer_i < read_cnt) {  
            /* Parse events and queue them. */  
            struct inotify_event* pevent = (struct inotify_event*)&buffer[buffer_i];  
            if (pevent->mask & IN_MODIFY) {  
                printf("config %s modified\n", kszConfigPath);  
                reparse_config();  
            } else {  
                printf("Unrecognized event mask %d\n", pevent->mask);  
            }  
            buffer_i += sizeof(struct inotify_event) + pevent->len;  
        }  
    }  
  
    /** Remove watch. */  
    if (inotify_rm_watch(inot_fd, watch_fd) < 0) {  
        printf("inotify_rm_watch error %d\n", errno);  
    }  
  
    return 0;  
}  

一、使用vi编辑a文件,然后保存。程序没有截获IN_MODIFY事件,打印的是Unrecongnized event mask 32768。
二、使用mv更改文件,也就是使用命令mv a a.bak。在mv后,发现程序没能截获IN_MODIFY事件,打印的是Unrecongnized event mask 32768。
三、删除a文件,并重启程序,发现inotify_init注册失败,而errno为2。
      看来,我的程序是有问题的。那么32768到底是什么呢?查看官方的inotify文档 inotify(7) - Linux manual page ,发现32768是IN_IGNORED,表示使用者对watch标识符调用了inotify_rm_watch,或者watch标识符被自动从inotify中移除。意思很明显,就是文件被删除了。inotify只是监视句柄,并非文件路径(实际上监听的是inode),当文件标示符对应的文件被删除时,这个文件标识符也被自动从inotify中移除,在以上的代码中,也就意味着inotify中没有任何watch标识符了,自然也就不会通知IN_MODIFY事件。
     等等,在第一个步骤中,我们不是修改了a文件吗?那至少应该打出IN_MODIFY吧。这里要解释一下,vi编辑时会先将文件保存为a.swap,等编辑完毕后再移动回来或者删除,具体移动回来还是删除,取决于是退出vi时是保存操作,还是取消操作。所以这里根本没有触发IN_MODIFY事件,实际上触发的是IN_MOVE_TO事件(a文件被移出),以及IN_IGNORED事件等等。

        另外,使用gedit等编辑工具打开文件,编辑后再保存,也会出现上述问题。原理一样。可以使用ll -i 查看一下文件对应的inode,发现根本不是同一个文件。但是如果用程序进行修改,则不会发生变更。
     这里要注意的是:文件被删除,不一定会产生IN_IGNORED事件,也就是说文件被删除时,不一定会把文件对应的标识符自动从inotify中删除。在这些情况下,我们需要重新对文件重新 注册 监控。
     在第三个测试中,我们看到inotify只会对存在的文件进行监控,如果要实现无论文件是否存在都持续监控,那么我们需要自动重新注册 监控 。
     以下是修改后的代码:

#include <errno.h>  
#include <stdio.h>  
#include <sys/inotify.h>  
  
static const char kszConfigPath[] = "/usr/local/path/to/config";  
static int s_running = 0;  
  
extern int reparse_config();  
  
#ifdef _WIN32  
struct inotify_event {  
    int      wd;       /* Watch descriptor */  
    uint32_t mask;     /* Mask of events */  
    uint32_t cookie;   /* Unique cookie associating related 
                         events (for rename(2)) */  
    uint32_t len;      /* Size of name field */  
    char     name[];   /* Optional null-terminated name */  
};  
  
int inotify_init(void);  
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);  
int inotify_rm_watch(int fd, int wd);  
#endif  
  
int set_non_blocking(fd)  
{  
    int fd_flag = 0;  
    if (fd < 0)  
        return EINVAL;  
    fd_flag = fcntl(fd, F_GETFL, 0);  
    if (fd_flag < 0)  
        fd_flag = 0;  
    return fcntl(fd, F_SETFL, fd_flag | O_NONBLOCK);  
}  
  
int rm_inotify_wd(int fd, int wd)  
{  
    char ignore[1024];  
    if (fd < 0 || wd < 0)  
        return EINVAL;  
    while (read(fd, ignore) > 0) {  
        /* Ignore previous unread notify events. */  
        continue;  
    }  
    return inotify_rm_watch(fd, wd);   
}  
  
int inotify_loop()  
{  
    int selret = 0;  
    int read_cnt = 0;  
    int inot_fd = -1;  
    int watch_fd = -1;  
    int need_parse = 0;  
    int need_remove_wd = 0;  
    unsigned int watch_flag = IN_CLOSE_WRITE | IN_CREATE | IN_DELETE  
        | IN_DELETE_SELF | IN_MOVE | IN_MOVE_SELF;  
    fd_set read_fds;  
    struct timeval seltime;  
    char buffer[16384];  
    int buffer_i = 0;  
  
    if ((inot_fd = inotify_init()) < 0) {  
        printf("inotify_init error %d\n", errno);  
        return -1;  
    }  
  
    if (set_non_blocking(inot_fd) < 0) {  
        printf("set_non_blocking error %d\n", errno);  
    }  
  
    while (s_running) {  
        new_init= 0;  
  
        if (watch_fd < 0) {  
            /* Watch config file. */  
            if ((watch_fd = inotify_add_watch(inot_fd, kszConfigPath, watch_flag)) < 0) {  
                printf("inotify_init error %d\n", errno);  
            } else {  
                /* Non-existed -> existed, reparse immediately.*/  
                reparse_config();  
            }  
        }  
  
        seltime.tv_sec = 1;  
        seltime.tv_usec = 0;  
        if (watch_fd < 0) {  
            selret = select(NULL, NULL, NULL, NULL, &seltime);  
        } else {  
            FD_ZERO(&read_fds);  
            FD_SET(inot_fd, &read_fds);  
            selret = select(inot_fd + 1, &read_fds, NULL, NULL, &seltime);  
        }  
          
        if (selret < 0) {  
            printf("select error %d\n", errno);  
            continue;  
        } else if (selret == 0) {  
            continue;  
        } else if (!FD_ISSET(inot_fd, &read_fds)) {  
            printf("inot_fd not in fdset\n");  
            continue;  
        }  
  
        read_cnt = read(fd, buffer, sizeof(buffer));  
        if (read_cnt <= 0) {  
            printf("read <= 0 (%d)\n", read_cnt);  
            continue;  
        }  
  
        need_parse = 0;  
        need_remove_wd = 0;  
        buffer_i = 0;  
        while (buffer_i < read_cnt) {  
            /* Parse events and queue them. */  
            struct inotify_event* pevent = (struct inotify_event*)&buffer[buffer_i];  
            if (pevent->mask & IN_MODIFY) {  
                printf("config %s modified\n", kszConfigPath);  
            } else if (pevent->mask & IN_CLOSE_WRITE) {  
                printf("config %s close for writing\n", kszConfigPath);  
                need_parse = 1;  
            } else if (pevent->mask & IN_CREATE) {  
                printf("config %s created\n", kszConfigPath);  
                need_parse = 1;  
            } else if (pevent->mask & (IN_DELETE | IN_DELETE_SELF)) {  
                printf("config %s was deleted\n", kszConfigPath);  
                need_parse = 1;  
                need_remove_wd = 1;  
            } else if (pevent->mask & (IN_MOVE | IN_MOVE_SELF)) {  
                printf("config %s was removed\n", kszConfigPath);  
                need_parse = 1;  
                need_remove_wd = 1;  
            } else {  
                printf("Unrecognized event mask %d\n", pevent->mask);  
            }  
            buffer_i += sizeof(struct inotify_event) + pevent->len;  
        }  
  
        if (need_parse) {  
            reparse_config();  
        }  
  
        if (need_remove_wd) {  
            int ret = rm_inotify_wd(inot_fd, watch_fd);  
            if (ret < 0) {  
                printf("rm_inotify_wd error %d\n", ret);  
            }  
            watch_fd = -1;  
        }  
    }  
  
    /** Remove watch. */  
    if (inotify_rm_watch(inot_fd, watch_fd) < 0) {  
        printf("inotify_rm_watch error %d\n", errno);  
    }  
  
    close(inot_fd);  
  
    return 0;  
}  

43、Linux 文件事件监控:inotify 机制详解 本文详细介绍了Linux系统中的inotify机制,用于实时监控文件和目录的事件,如创建、删除、修改和重命名等。文章涵盖inotify的基本操作、事件类型、事件读取流程、队列限制,并与旧的dnotify机制进行对比,突出其优势。同时提供示例代码,展示如何使用inotify结合nftw函数递归监控目录及其子目录的文件事件,适用于日志记录、实时备份等应用场景。 阅读详情

相关推荐

cJSON库用完后内存泄漏?手把手教你正确使用cJSON_Delete和free释放资源

本文深入解析cJSON库的内存管理机制,重点解决使用后的内存泄漏问题。通过详细讲解cJSON_Delete和free的正确使用方法,帮助开发者在处理JSON数据时有效释放资源,避免内存泄漏。特别适用于嵌入式系统和资源受限环境,提升开发效率和系统稳定性。

weixin_30808693的博客 417

linux 监控某个文件IN_MODIFY

文件监控

sun007700的专栏 437

基于STM32F407的DDS信号源设计与实现:DAC+DMA+TIM的高精度波形生成

本文详细介绍了基于STM32F407微控制器实现高精度DDS信号源的完整方案。通过结合DAC、DMA和TIM外设,并运用DDS算法,实现了分辨率高达0.001Hz的波形生成。文章深入剖析了相位累加器、波表查表等核心原理,提供了从CubeMX配置到代码实战的完整指南,并探讨了性能优化与高频输出问题的解决方案。

weixin_29193259的博客 501

Unix/Linux编程:监控文件事件

某些应用程序需要对文件或目录进行监控,已侦测是否发生了特定事件。比如,当把文件加入或者移除一目录树时,图形化文件管理器应该能判断此目录是否在当前显示之列;守护进程可能想要监控自己的配置文件,以了解其是否被修改 自内核2.6.13起,Linux开始提供 inotify 机制,以允许应用程序监控文件事件 inotify 机制所取代的是 dnotify,后者一来较为陈旧,二来仅具备前者的部分功能 概述 使用inotify API由如下几个关键步骤 应用程序使用 inotify_init()来创建一 inot.

OceanStar的博客 1094

海思MPP平台千兆以太网驱动集成与测试指南

千兆以太网(GE)是嵌入式视觉系统中音视频流传输与边缘控制的核心物理接口,其底层驱动需兼顾DMA高效调度、中断实时响应与PHY链路稳定性。在Linux嵌入式环境中,GE驱动通常基于net_device框架实现,并深度耦合于SoC专用媒体处理平台(如海思MPP),通过硬件抽象层(HAL)屏蔽寄存器操作,提供零拷贝、RGMII时序适配、描述符环管理等关键能力。技术价值体现在降低CPU负载、保障1080p@30fps实时推流、支持ONVIF/RTSP协议交互及固件远程升级。典型应用场景包括网络摄像机(IPC)、A

weixin_42599908的博客 88

跟着团子学SAP PS:项目计划成本与项目预算设计思路

​在做SAP PS模块概览培训的时候,很多时候都会用到下面的一页PPT片子讲到项目计划成本与预算的关系,但真正这幅片子想表达的具体内容甚至大部分PS顾问也是无法阐明清楚的。 而SAP PS的设计其实是在丰富的项目管理学的基础上而开展的,所以今天从业务上结合SAP中的功能点讲一下项目成本计划及预算的逻辑。 在项目管理的十大知识领域中,项目成本管理主要是包括为使项目在批准的预算内完成而对成本进行规划...

Lucas1211的博客 3万+

linux 监控某个文件

可监控的事件 有几种事件能够被监控。一些事件,比如 IN_DELETE_SELF 只适用于正在被监控的项目,而另一些,比如 IN_ATTRIB 或者 IN_OPEN 则只适用于监控过的项目,或者如果该项目是目录,则可以应用到其所包含的目录或文件。 IN_ACCESS 被监控项目或者被监控目录中的条目被访问过。例如,一个打开的文件被读取。 IN_MODIFY 被监控项目或者被监控目录中的条...

州官已放火的博客 1416

如何用 OBProxy 实现 OceanBase 的最佳路由策略

本文基于业务中一类常见的路由问题展开,对OceanBase的路由功能及对应配置项做详细介绍,指导用户在业务实践中如何正确的配置路由策略及快速排查路由问题

OceanBase分布式数据库的博客 1679

linux inotify设备文件,Linux下使用inotify实现对文件的监控

项目中,要实现用户通过网页设置參数,后台接收数据然后写串口。网页写数据到本地文件,使用inotify监控文件的IN_MODIFY事件。当文件被改动,然后触发写串口事件。第一个程序只把要监控的文件增加watch_list中,运行程序。发现select返回。只能检測到文件被改动,可是假设同一时候监控多个文件。却不能区分是哪个文件被修改了。/*This is the sample program to ...

weixin_39616798的博客 315

Rocky Linux 9.0国内yum源一键替换指南(上海交大镜像站实测)

本文详细介绍了如何将Rocky Linux 9.0的yum源替换为上海交大镜像站,以解决国内用户访问官方源速度慢的问题。通过实测验证,上海交大镜像站提供高速下载和稳定更新,并提供了完整的替换步骤、常见问题解决方案及高级优化配置,帮助用户提升软件包下载效率。

weixin_29304985的博客 357

Python图像处理之光斑分析

光斑是工程中经常出现的图像数据,其特点是目标明确,分布清晰。对光斑图像的分析,主要包括质心定位、目标截取以及半径拟合等。

微小冷的学习笔记 2855

linux inotify函数,inotify使用范例

#include /** struct inotify_event - structure read from the inotify device for each event** When you are watching a directory, you will receive the filename for events* such as IN_CREATE, IN_DELETE, I...

weixin_28877107的博客 442

基于A*算法的二维栅格地图路径规划与避障MATLAB仿真

本文将介绍如何使用MATLAB编写基于A*算法的二维栅格地图路径规划与避障的仿真程序。我们可以使用二维数组表示地图,其中每个元素表示一个栅格的状态,例如1表示障碍物,0表示可行走区域。接下来,我们需要定义A*算法的节点类。G值表示从起始节点到当前节点的代价,H值是启发式函数估计的从当前节点到目标节点的代价。算法,我们可以有效地找到起始点到目标点的最短路径,并在仿真中进行可视化展示。算法的基本思路是从起始节点开始,按照一定的启发式函数选择下一个节点,直到找到目标节点或者遍历完所有可行走区域。

ByteWhizX的博客 1272

关于使用inotify监测文件目录发现的问题

使用inotify监测文件系统时需要注意的的事项

qq_45688637的博客 565

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Linux操作系统中的事件通知机制 - 监控文件事件 inotify

使用 inotify API 有以下几个关键步骤1。应用程序使用 inotify_init()来创建 inotify 实例,该系统调用所返回的文件描述符用于在后续操作中指代该实例。应用程序使用 inotify_add_watch()向 inotify 实例(由步骤 1 创建)的监控列表添加条目,以告知内核哪些文件是自己的兴趣所在。每个监控项都包含一个路径名以及相关的位掩码。位掩码针对路径名指明了所要监控的事件集合。

Android系统工程师--小馬佩德罗 1438

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Linux应用 inotify监控文件变化

本文讲述了inotify的定义、编程步骤以及常用接口,并编写程序进行测试

cesheng3410的博客 3295

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Linux文件监控机制 inotify

/* * 监控文件事件, 自内核2.6.13起, Linux开始提供inotify机制, 以允许应用程序监控文件事件 * inotify机制可用于监控文件或目录, 当监控目录时, 与路径自身及其所含文件相关的事件都会通知给应用程序 * inotify监控机制为非递归, 若应用程序有意控制整个目录子树内的事件, 需要对该树中的每个目录发起inotify_add_watch()调用 * (即只...

忧桑的小兔子 1573

用于监控 Linux文件系统事件的inotify接口学习笔记

<br /><br /><br /> 参考<br /> http://www.ibm.com/developerworks/cn/linux/l-inotify/index.html<br /><br /> 从 Linux 2.6.13 内核开始,Linux 就推出了 inotify,允许监控程序打开一个独立文件描述符,并针对事件集监控一个或者多个文件,例如打开、关闭、移动/重命名、删除、创建或者改变属性。<br />提供的接口<br />inotify提供的结构和接口(见/usr/include/s

wdwbw的专栏 1165

inotify文件监控

参考:xxxx /*************************************************************************\* Copyright (C) Michael Kerrisk, 2018. ** ...

weixin_30596023的博客 267

inotify的用法实践

inotify可用来监听句柄,从而知道文件是否被更改,删除,等 监控文件被修改和删除,重新读取数据的代码如下. 值得注意的要点是,ubunutu中很多修改的文件策略都是删除,再重建(比如用vi编辑一个文件) 当文件删除/再重建后,将收到 IN_DELETE_SELF 消息和 IN_IGNORED 消息, 而后 watch_id 将被从 add_watch中移出,以后再收不到消

span76的专栏 2021

高效的文件监控框架inotify-tools

inotify-tools是由Red Hat开发的一款Linux文件系统监控工具,该工具高效率、细粒度、异步地对用户空间文件进行安全、高性能的监控,而在linux中一切皆文件,这就意味着你可以对设备,网络,CPU等等都可以进行监控,并做一定的action。inotify-tools内包含的so文件可以直接被复用在自己的项目工程中,或者直接使用工具内编译好的进程,inotifywait和inotifywatch进行文件监控,监控粒度非常细甚至软连接//被监控项目或者被监控目录中的条目被访问过。

诗酒趁年华 922

如何使用inotify_init,inotify_add_watch,inotify_rm_watch,read编写监控程序

如何使用inotify_init,inotify_add_watch,inotify_rm_watch,read编写监控程序 标签: structeventsnullpathaccess2010 2011-10-10 19:22 3824人阅读 评论(0) 收藏 举报  分类: kernel与Android(131)    http://blog.163.

hanjw05的博客 4297

inotify手册

如果先前已将pathname加入fd的监控列表,则会修改现有pathname监控项的掩码,并返回其监控描述符。会在列表中创建一个新的监控项,并返回一个新的、非负的监控描述符,用来在后续操作中指代此监控项。之所以这么做,是因为很多应用程序都并不关注同一时间的反复出现,而其丢弃多余的事件能降低内核维护事件队列所需的内存总量。会从文件描述符fd所指代的 inotify实例中,删除由wd所定义的监控项。,会返回其所指代的inotify实例中的当前可读字节数。既可以追加新的监控项,也可以修改现有监控项。

随手写下笔记和感悟 433

现代C++封装inotify与select实现高效文件监控方案

文件系统监控是Linux开发中的常见需求,传统轮询方案实时性差且资源消耗大。inotify作为内核提供的事件驱动机制,能够高效监控文件变化并即时通知应用。结合I/O多路复用技术如select,可以实现单线程同时监控多个文件描述符,构建非阻塞的异步事件处理模型。这种方案在现代C++工程实践中,通过RAII机制和类型安全封装,将底层系统调用转化为安全易用的高级抽象,广泛应用于日志分析、配置热重载等场景。本文深入探讨了如何利用select与inotify实现一套符合现代C++设计哲学的文件监控组件,涵盖核心封装、

weixin_30258901的博客 407

Linux inotify功能 使用 详解示例

linux inotify的介绍, 详解, 以及代码示例

netlogs的博客 1390

Linux目录、文件事件监控

之前开发一些Linux程序的时候,想要监控文件或者文件夹被改动的事件,但是没有找到方法。今天在看Linux/UNIX系统编程手册这本书的时候,看到有一章专门讲监控文件事件,才顿悟自己还是看书看的太少了。因此写这部博客记录一下,后续若用到,也好马上用到。 从内核2.6.13起,Linux就提供了inotify机制,以允许应用程序监控文件事件。inotify主要有以下API: ...

rfasm的专栏 945
上一篇: 年月日时分秒的正则表达式
下一篇: 关于inotify监控mmap内存映射或共享内存相关变动的做法
土豆西瓜大芝麻
博客等级 码龄18年 1189粉丝 238原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值