判断信息是否发生变化决定是否需要保存

本文介绍了一种用于检测网页表单是否发生改变的机制。通过比较新旧表单元素对象集合来判断用户输入是否发生变化,并提供了完整的实现代码。此方法适用于需要监控表单状态的应用场景。
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div class="main">
    <div class="baseInfo">
        <div class="form-item"  data-belong="baseInfo" data-type="input" id="text1">
            <div class="form-item-caption">text1:</div>
            <div class="form-item-control">
                <input type="text" value="text1"/>
            </div>
        </div>
        <div class="form-item"  data-belong="baseInfo" data-type="select" id="select1">
            <div class="form-item-caption">select1:</div>
            <div class="form-item-control">
                <select name="">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
            </div>
        </div>
    </div>

    <div class="otherInfo">
        <div class="form-item" data-belong="otherInfo" data-type="input" id="text2">
            <div class="form-item-caption">text2:</div>
            <div class="form-item-control">
                <input type="text" value="text2"/>
            </div>
        </div>
        <div class="form-item" data-belong="otherInfo" data-type="input" id="text3">
            <div class="form-item-caption">text3:</div>
            <div class="form-item-control">
                <input type="text" value="text3"/>
            </div>
        </div>
        <div class="form-item"  data-belong="otherInfo" data-type="select" id="select2">
            <div class="form-item-caption">select2:</div>
            <div class="form-item-control">
                <select name="">
                    <option value="1">1</option>
                    <option value="2" selected>2</option>
                    <option value="3">3</option>
                </select>
            </div>
        </div>
    </div>
</div>
<button id="submitBtn">提交</button>
<script src="js/jquery.min.js"></script>
<script>
    var globalOldFormItemObjSet =  {
        baseInfo: [],
        otherInfo: []
    };
    $('#submitBtn').click(function(){
        if(!isFormChange(globalOldFormItemObjSet, getFormItemObjList())) {
            alert('没有发生变化');
        } else {
            alert('发生变化了');
        }
        globalOldFormItemObjSet = getFormItemObjList();
    })
    function getFormItemObjList() {
        var formItemObjSet = {
            baseInfo: [],
            otherInfo: []
        };
        var formItemObj = $('.main').find('.form-item');
        formItemObj.each(function(index, item) {
            var formItemObj = getFormItemObj(item);
            var Key = $(item).attr('data-belong');
            formItemObjSet[Key].push(formItemObj);
        });
        return formItemObjSet;
    }
    function getFormItemObj(formItem) {
        var item = $(formItem);
        var control = item.find('.form-item-control');
        var caption = item.find('.form-item-caption');
        var type = item.attr('data-type');
        var id = item.attr('id');
        var text= '';
        switch (type) {
            case 'input':
            {
                text = control.find('input').val().trim();
                break;
            }
            case 'select':
            {
                text = control.find('select').val();
                break;
            }
        }
        return {
            id: id,
            type: type,
            text: text
        };
    }
    function isEqualObj(a,b){
        var aProps = Object.getOwnPropertyNames(a);
        var bProps = Object.getOwnPropertyNames(b);
        if(aProps.length !== bProps.length){
            return false;
        }
        for(var i=0;i<aProps.length;i++){
            if(a[aProps[i]] != b[aProps[i]]) {
                return false;
            }
        }
        return true;
    }
    function isFormChange(oldFormItemObjSet,newFormItemObjSet) {
        var array = Object.keys(newFormItemObjSet);
//        console.log(array); // ["baseInfo", "otherInfo"]
        for(var i=0;i<array.length;i++) {
            var keyArray = array[i];
            var newKeyArray = newFormItemObjSet[keyArray];
            // console.log(newKeyArray); [{},{}]
            for(var j=0;j<newKeyArray.length;j++){
//                console.log(newKeyArray[j]);
               //  console.log(keyArray); // baseInfo
               //  console.log(oldFormItemObjSet[keyArray]) []
                if((oldFormItemObjSet[keyArray].length == 0 && array.length != 0) || (oldFormItemObjSet[keyArray].length != 0 && array.length == 0)) {
                    return true;
                } else {
                    var isSame = isEqualObj(newKeyArray[j], oldFormItemObjSet[keyArray][j]);
                    if (!isSame) {
                       return true;
                    }
                }
            }
        }
        return false;
    }
</script>
</body>
</html>
<think>嗯,用户现在聚焦于Cortex-M33异常处理中栈选择机制的具体实现细节。结合用户提供的站内引用和之前讨论的异常处理框架,需要重点解析硬件自动选择栈指针的逻辑规则。 用户引用的资料[1]提到MPU对栈溢出的硬件检测机制,这与栈指针的有效性直接相关;资料[2]中关于R13双栈指针的说明是理论基础;资料[4]则补充了TrustZone场景下安全状态对栈选择的影响。这些恰好构成完整的判断链条。 从技术实现看,Cortex-M33的栈选择本质是状态机行为:当异常发生时,处理器通过三个维度确定栈指针: 1. 当前运行模式(线程/Handler)决定基础栈类型 2. CONTROL寄存器bit[1]指示线程模式下的栈指针(PSP/MSP) 3. 安全状态位决定使用安全栈还是非安全栈 特别值得注意的是TrustZone带来的变化——当发生安全状态切换时(如非安全态触发安全异常),硬件会自动将EXC_RETURN的bit[6]置位,此时现场保存会跳转到对应安全等级的栈空间。资料[4]提到的"惰性FPU保存"优化其实也依赖于此机制,因为安全状态切换时需要隔离浮点寄存器存储区域。 用户可能隐含的关注点在于:如何确保中断嵌套时的栈选择一致性?这需要解释NVIC的优先级管理机制——高优先级异常抢占时,处理器会继承被中断异常的栈选择策略,但安全状态切换会强制刷新栈指针。这也能解释为什么资料[1]强调MPU对栈区域的保护至关重要,因为错误的栈指针选择可能导致MPU触发HardFault。 最后需要提醒编译器层面的影响:__attribute__((naked))修饰的中断服务函数会跳过编译器生成的栈操作代码,此时必须用汇编显式处理栈切换。这点虽然未在引用资料中体现,但实际开发中极易引发问题。</think>Cortex-M33 处理器在异常处理期间**通过硬件自动判断使用主栈(MSP)或进程栈(PSP)进行现场保存**,其决策逻辑基于以下关键机制: --- ### 一、栈选择的核心规则 1. **运行模式决定栈类型** - **线程模式(Thread Mode)**: - 使用 `CONTROL` 寄存器 **bit[1]** 选择栈指针: - `0` → 使用 **主栈(MSP)**(默认) - `1` → 使用 **进程栈(PSP)**(需手动配置) - **处理模式(Handler Mode)**: - **强制使用主栈(MSP)**,无视 `CONTROL` 寄存器配置[^2]。 2. **异常触发时的自动切换** - 当异常(如中断)发生时: - 若从 **线程模式** 进入异常 → 保存现场到 **当前线程模式使用的栈**(MSP 或 PSP) - 若从 **处理模式** 进入异常(嵌套中断)→ 保存现场到 **主栈(MSP)** - 异常处理结束后,硬件根据 **EXC_RETURN 值** 自动恢复之前的栈模式[^1]。 --- ### 二、TrustZone 安全扩展的影响 1. **双栈指针机制** - 安全状态与非安全状态各有独立的 **MSP_S/MSP_NS** 和 **PSP_S/PSP_NS**。 - 异常触发状态切换时(如非安全态调用安全服务),硬件自动选择对应安全等级的栈指针[^4]。 2. **安全网关(SG)调用** - 非安全代码通过 `SG` 指令进入安全态时: - 硬件自动将部分寄存器保存到 **非安全栈(PSP_NS/MSP_NS)** - 安全异常处理使用 **安全主栈(MSP_S)** --- ### 三、现场保存流程详解 #### 步骤 1:硬件自动选择栈 ```mermaid graph TD A[异常发生] --> B{当前模式} B -->|线程模式| C[使用 CONTROL 指定的栈 PSP/MSP] B -->|处理模式| D[强制使用 MSP] C --> E[保存现场到选定栈] D --> E ``` #### 步骤 2:现场保存内容 硬件自动压入当前栈的寄存器: - **基础帧**:`R0-R3, R12, LR, PC, xPSR` - **扩展帧(FPU 启用时)**:`S0-S15, FPSCR` - **安全切换时**:额外保存 `LR_monitor`(监控模式链接寄存器)[^4] #### 步骤 3:栈切换示例 ```c // 非安全线程模式使用 PSP_NS void NonSecure_Function() { __asm("SVC #0"); // 触发 SVC 异常 } // SVC 异常处理(安全态) void SVC_Handler(void) { // 硬件自动行为: // 1. 从 PSP_NS 保存现场 → 安全栈 MSP_S // 2. 切换为 MSP_S 并进入安全处理模式 } ``` --- ### 四、关键寄存器控制 | 寄存器 | 功能说明 | |--------------|--------------------------------------------------------------------------| | `CONTROL` | Bit 1 = 0:线程模式用 MSP<br>Bit 1 = 1:线程模式用 PSP | | `EXC_RETURN` | Bit 2 = 0:返回后使用 MSP<br>Bit 2 = 1:返回后使用 PSP<br>Bit 6 = 1:返回非安全态 | | `VTOR` | 配置安全/非安全向量表基址,决定异常入口 | > ⚠️ **栈溢出保护**:若栈指针超出 MPU 配置的有效区域(如 PSP 越界),将触发 **MemManage Fault** 或 **HardFault**[^1]。 --- ### 五、调试建议 1. **检查 `EXC_RETURN` 值** - `0xFFFFFFF1`:返回 Handler 模式,用 MSP - `0xFFFFFFFD`:返回线程模式,用 PSP - `0xFFFFFFED`:返回线程模式(非安全),用 PSP 2. **MPU 配置验证** 确保栈区域 MPU 属性匹配(例如:线程模式 PSP 区域需配置为可读写)。 --- ### 相关问题 1. **如何在 Cortex-M33 中配置 MPU 以防止栈溢出?请描述具体步骤** 2. **TrustZone 安全切换时,哪些寄存器会被硬件自动保存到非安全栈?** 3. **当嵌套中断发生时,为什么必须强制使用 MSP 保存现场?** 4. **如何通过 `EXC_RETURN` 值判断异常返回后的安全状态?** [^1]: Cortex-M3 栈溢出检测机制 [^2]: Cortex-M3 寄存器与操作模式 [^4]: TrustZone 安全扩展与惰性栈保存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值