示例分析CALL/CC

[b]说明:此文内容纯属个人看法,无所谓对错,欢迎拍砖![/b]

call/cc(call-with-current-continuation)是函数编程的顶级概念,虽然我们用到的场合不是太多,但重要性非比寻常。

call/cc的魔力在于迫使运行的程序通过时光隧道返回到从前的某个时刻。

从返回的方式看我们可以有多种理解,下面是最常见的两种:

1、逐步退回:一步就是一次函数调用。执行call/cc时,程序自动执行函数返回操作(RET),直到返回的位置与call/cc的要求一致。

2、直接跳转:程序的指令系统直接切换到call/cc的位置,被抛掉的运行节点和对象留给GC处理。

命令式语言(如C/C++/PASCAL)可以很方便的实现第一种返回方式,比如使用异常和长跳转,但由于基于栈运行和缺乏GC支持,很难实现第二种返回方式。我们不在这里讨论语言的分类问题,而是通过一个例子,凭想象来推测一下call/cc背后都发生了些什么。

[code]
void b(function cc)
{
= "b";
cc(); // 返回a()中创建cc对象后的下一条指令位置
}

void a()
{
callcc cc() { = "a" }; // 创建cc对象,这里是个函数
b(cc);
}
[/code]

现在运行函数a()并登记其运行过程:

[code]
a()开始执行。
|__创建call/cc函数cc()。
|__跳过cc()的返回执行代码。
|__将cc传递到b()函数中加以执行。
...b()开始执行
...|__打印字符'b'
...|__执行cc()。
......cc()开始执行
......|___中断b()回到a(),指令系统指向cc()的返回执行代码。
|__打印字符'a'
|__回到步骤4。
[/code]

cc在第9步直接中断b()的运行回到a()并将指令系统指向cc()的返回执行代码,这一步是怎么发生的呢?

一、可以确定cc至少携带了以下两个信息:

1、a()的运行时信息(比如栈基址)。
2、cc返回执行代码的地址。

我们用结构ccrec来描述它们:

[code]
// call/cc 信息结构定义
struct ccrec {
void * addr; // 函数运行时信息的地址
unsigned long next; // 下一条指令的位置
}
[/code]

指令系统可以假定为以下设计:

[code]
// 指令系统结构定义
struct isrec {
struct ccrec current; // 当前运行时信息
struct isrec *prior; // 上一级调用的指令系统结构
}

struct isrec *current; // 当前指令系统结构

void *get_call_base() // 提取当前运行时信息
{
return current->current.addr;
}

unsigned long get_next_instruction() // 返回下一条指令的地址
{
return current->current.next;
}

void set_next_instruction(unsigned long next) // 设置下一条指定的地址
{
current->current.next = next;
}
[/code]

二、callcc创建cc对象后在其内部保存以上两条信息:

[code]
// callcc 指令的处理过程
void * cc = create_function(); // 创建 cc 函数
struct ccrec * rec = create_new_ccrec();
rec->addr = get_call_base(); // 提取当前函数运行时信息
rec->next = get_next_instruction(); // 下一条即将执行的的指令
save_ccrec(cc, rec); // 保存信息到cc对象
[/code]

三、cc执行时重置了指令系统的状态:

[code]
// 提取call/cc返回信息

struct ccrec * rec = load_ccrec(cc);

// 执行返回操作

while (rec->addr != get_call_base())
exec_return(); //==> 它做了什么?

// 设置下一条指令

set_next_instruction(rec->next);
[/code]

函数exec_return()不应该太复杂,大致的是如下的代码:

[code]
// 函数返回操作函数
void exec_return()
{
// 1. 加锁休眠垃圾回收并返回当前指令系统结构
struct isrec * curr = begin_return();

// 2. 将当前运行时信息登记到垃圾回收系统
gc_notify(curr);

// 3. 设置指令系统,返回上一级调用
current = curr->prior;

// 4. 完成返回操作后开锁,重新激活垃圾系统
end_return();
}
[/code]

这样的想象和推测单从理解上说应该是完备的,基于这个理解,我们试着做些更深层次的发挥。
<think>我们正在使用CC2642芯片,并希望实现主机(Central)的安全连接功能。参考之前关于blecent的示例(基于ESP-NimBLE),我们需要将其适配到CC2642的BLEStack上。CC2642使用的是TI的BLEStack,相关API和配置与NimBLE有所不同,但安全连接的核心概念是相通的。###主要步骤和代码结构1.**初始化BLEStack**:包括GAP、GATT和安全管理器(SM)。2.**配置安全参数**:设置绑定、MITM保护、安全连接(LESecureConnections)以及输入输出能力。3.**启动扫描**:扫描支持安全连接的从设备。4.**建立连接**:当发现目标设备时,发起连接。5.**启动加密**:在连接建立后,启动安全加密过程。6.**处理安全事件**:在回调函数中处理加密状态变化、配对请求等事件。###示例代码(基于TIBLEStack)以下是基于TISimpleLinkSDK的示例代码框架(使用主机角色):```c#include<ti/sysbios/knl/Clock.h>#include<ti/sysbios/knl/Task.h>#include<ti/drivers/PIN.h>#include<ti/drivers/pin/PINCC26XX.h>/*BLEStack头文件*/#include<icall.h>#include<util.h>#include"bcomdef.h"#include"simple_central.h"#include"gatt.h"#include"hci.h"#include"gap.h"#include"gapgattserver.h"#include"gapbondmgr.h"//安全管理和绑定//全局变量staticuint16_tconnHandle=GAP_CONNHANDLE_INIT;//连接句柄//安全相关回调staticgapBondCBs_tbondMgrCBs={NULL,//pairStateCB-配对状态回调NULL,//passcodeCB-配对码回调(如果需要)NULL//oobCB-带外数据回调};//GAP事件回调函数staticuint32_tsimpleCentral_ProcessGAPMsg(gapEventHdr_t*pMsg){switch(pMsg->opcode){caseGAP_DEVICE_INIT_DONE_EVENT://设备初始化完成,开始扫描GAPCentralRole_StartDiscovery(DEFAULT_DISCOVERY_ACTIVE_SCAN,DEFAULT_DISCOVERY_WHITE_LIST);break;caseGAP_DEVICE_DISCOVERED_EVENT:{gapDeviceDiscoveryEvent_t*pDiscoverEvent=(gapDeviceDiscoveryEvent_t*)pMsg;//检查设备是否支持安全连接(在广播标志中)if(pDiscoverEvent->eventData.deviceInfo.advertisingFlags&GAP_ADTYPE_FLAGS_SECURE_CONN){//这里可以进一步检查设备名或服务UUID等//发起连接GAPCentralRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,DEFAULT_LINK_WHITE_LIST,pDiscoverEvent->eventData.deviceInfo.addr,pDiscoverEvent->eventData.deviceInfo.addrType);}break;}caseGAP_LINK_ESTABLISHED_EVENT:{gapEstLinkReqEvent_t*pEstLinkReqEvent=(gapEstLinkReqEvent_t*)pMsg;connHandle=pEstLinkReqEvent->connHandle;//连接建立后,启动加密过程GAPBondMgr_SendPairReq(connHandle);break;}caseGAP_PAIRING_STATE_CHANGE_EVENT:{gapPairingStateEvent_t*pStateEvent=(gapPairingStateEvent_t*)pMsg;if(pStateEvent->state==GAPBOND_PAIRING_STATE_COMPLETE){if(pStateEvent->status==SUCCESS){//配对成功,安全连接建立//可以开始安全的数据传输}else{//配对失败}}break;}//其他事件处理...}return(SUCCESS);}//主任务函数staticvoidSimpleCentral_taskFxn(UArga0,UArga1){//初始化BLEStackICall_init();//初始化GAP层GAP_Init(GAP_PROFILE_CENTRAL);//初始化GATT层GATT_Init();//初始化GAPBondManager(安全管理器)//设置安全参数uint8_tpairMode=GAPBOND_PAIRING_MODE_INITIATE;uint8_tmitm=TRUE;//启用MITM保护uint8_tbonding=TRUE;//启用绑定uint8_tioCap=GAPBOND_IO_CAP_KEYBOARD_ONLY;//设备输入能力(键盘)//配置安全参数GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE,sizeof(uint8_t),&pairMode);GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION,sizeof(uint8_t),&mitm);GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED,sizeof(uint8_t),&bonding);GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES,sizeof(uint8_t),&ioCap);//注册回调GAPBondMgr_Register(&bondMgrCBs);//启动GAPCentralRoleGAPCentralRole_StartDevice(&simpleCentral_ProcessGAPMsg);//启动主循环while(1){//处理事件等}}intmain(){//硬件初始化Board_init();//创建任务Task_ParamstaskParams;Task_Params_init(&taskParams);taskParams.stackSize=TASK_STACK_SIZE;Task_create(SimpleCentral_taskFxn,&taskParams,NULL);//启动BIOSBIOS_start();return0;}```###关键配置说明1.**安全参数**:-`GAPBOND_PAIRING_MODE`:设置为主机发起配对(`GAPBOND_PAIRING_MODE_INITIATE`)。-`GAPBOND_MITM_PROTECTION`:启用中间人保护(设置为`TRUE`)。-`GAPBOND_BONDING_ENABLED`:启用绑定(设置为`TRUE`),这样会保存长期密钥(LTK)用于重连。-`GAPBOND_IO_CAPABILITIES`:设置设备的输入输出能力。这里设置为键盘(`GAPBOND_IO_CAP_KEYBOARD_ONLY`),表示设备可以输入配对码。2.**安全过程**:-连接建立后,调用`GAPBondMgr_SendPairReq(connHandle)`发起配对。-在`GAP_PAIRING_STATE_CHANGE_EVENT`事件中,通过`state`和`status`判断配对结果。###绑定信息管理绑定信息(LTK)会在配对成功后自动存储在Flash中。当重新连接时,如果双方都保存了绑定信息,则使用LTK直接加密连接,无需再次配对。查询绑定信息可以使用`GAPBondMgr_GetBondedDevices`函数。在建立连接后,如果发现目标设备已绑定,则可以使用`GAPBondMgr_EstablishLink`快速建立安全连接。###安全级别通过`GAPBondMgr_EstablishLink`建立的连接具有以下安全级别:-`1`:无安全(未加密)-`2`:有加密但未认证(无MITM)-`3`:加密且认证(MITM保护)-`4`:LE安全连接(使用ECDH)###注意事项-**配对码处理**:如果设备设置为需要输入配对码(例如,当IO能力设置为`GAPBOND_IO_CAP_DISPLAY_ONLY`时,需要显示器显示配对码),则需要在`passcodeCB`回调中处理。在上述代码中,我们设置IO能力为键盘输入(`GAPBOND_IO_CAP_KEYBOARD_ONLY`),这意味着设备可以输入配对码(比如通过串口接收用户输入)。-**错误处理**:在配对过程中,可能出现的错误包括:-`BLE_SM_ERR_PASSKEY_ENTRY`:配对码错误-`BLE_SM_ERR_CONFIRM_MISMATCH`:配对码确认失败-`BLE_SM_ERR_DISCONNECT`:连接断开这些错误可以在`GAP_PAIRING_STATE_CHANGE_EVENT`事件中检查,并通过`status`字段获取。###相关问题1.**如何在CC2642上实现配对码的输入?**[^1]2.**如何配置CC2642以同时支持传统配对和安全连接?**[^1]3.**如何管理已绑定的设备列表?**4.**安全连接失败时如何重试?**5.**在CC2642上使用安全连接对电池寿命的影响如何?**[^2]</think> 以下是在CC2642设备上作为主机实现BLE安全连接的示例代码与关键步骤解析,基于TI SimpleLink SDK开发环境: ### 关键代码示例 ```c #include <ti/sysbios/knl/Task.h> #include <ti/drivers/PIN.h> #include <ble_user_config.h> #include <gatt.h> #include <hci.h> #include <gap.h> #include <gapgattserver.h> #include <gapbondmgr.h> // 安全管理核心模块 // 安全配置参数 static gapBondParams_t securityParams = { .pairMode = GAPBOND_PAIRING_MODE_INITIATE, // 主机发起配对 .mitm = TRUE, // 启用MITM保护 .bonding = TRUE, // 启用绑定 .ioCap = GAPBOND_IO_CAP_KEYBOARD_ONLY, // 主机输入能力 .secureConnections = TRUE // 使用ECDH安全连接[^1] }; // 连接句柄 static uint16_t connHandle = GAP_CONNHANDLE_INIT; // 安全事件回调 static void securityEventHandler(uint8 event, void* pData) { gapBondEvent_t* bondEvent = (gapBondEvent_t*)pData; switch(event) { case GAPBOND_PAIRING_STATE_CHANGED_EVENT: if(bondEvent->state == GAPBOND_PAIRING_STATE_COMPLETE) { if(bondEvent->status == SUCCESS) { // 安全连接建立成功 Display_print0(display, 0, 0, "安全连接建立 (AES-CCM)"); // 此时可执行安全操作(如读取加密特征值) GATT_ReadCharValue(connHandle, 0x0012, 0); } else { // 安全连接失败处理 Display_print1(display, 0, 0, "安全错误: 0x%02x", bondEvent->status); } } break; case GAPBOND_PASSCODE_NEEDED_EVENT: // 需要输入配对码逻辑 Display_print0(display, 0, 0, "请输入设备显示的6位配对码"); // 实际应用中可读取串口输入 uint32_t passkey = 123456; // 示例配对码 GAPBondMgr_PasscodeRsp(connHandle, SUCCESS, passkey); break; } } // 主任务初始化 void mainTaskFxn(UArg a0, UArg a1) { // 初始化BLE协议栈 ICall_init(); GAP_Init(GAP_PROFILE_CENTRAL); GATT_Init(); // 配置安全管理器 GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t), &defaultPasscode); GAPBondMgr_Register(&securityParams, securityEventHandler); // 启动扫描安全设备 GAPCentralRole_StartDiscovery(DEFAULT_DISCOVERY_ACTIVE_SCAN, 0); while(1) { // 主循环处理事件 ICall_wait(ICALL_TIMEOUT_FOREVER); } } ``` ### 安全连接步骤 ```mermaid graph TD A[BLE初始化] --> B[配置安全参数] B --> C{开始扫描} C --> D[发现支持安全连接的设备] D --> E[发起连接请求] E --> F[调用GAPBondMgr_SendPairReq] F --> G{配对流程} G -->|需要MITM| H[输入配对码] G -->|安全连接| I[ECDH密钥交换] I --> J[AES-CCM数据加密] J --> K[加密通信建立] ``` ### 关键API说明 | API 函数 | 功能描述 | |---------|---------| | `GAPBondMgr_SetParameter()` | 设置安全参数(MITM/绑定/IO能力) | | `GAPBondMgr_SendPairReq()` | 主动发起配对请求 | | `GAPBondMgr_PasscodeRsp()` | 响应配对码输入 | | `GAPCentralRole_EstablishLink()` | 建立物理层连接 | | `GAP_GetParamValue(GAP_PARAM_LINK_SECURITY)` | 获取当前连接安全级别 | ### 安全级别对应表 | 值 | 安全级别 | 加密方式 | |---|----------|---------| | 0 | No Security | 无加密 | | 1 | Unauthenticated | LE Legacy (CTR) | | 2 | Authenticated | LE Legacy (CTR+CBC) | | 3 | Secure Connection | LE Secure (AES-CCM) [^1] | ### 绑定信息管理 ```c // 获取已绑定设备列表 uint8_t bondedDevices[MAX_BONDED_DEVICES]; uint8_t count = GAPBondMgr_GetBondedDevices(bondedDevices); // 尝试快速安全连接 if (GAPBondMgr_IsDeviceBonded(devAddr)) { // 直接使用绑定信息加密 gapBond_t bondInfo; GAPBondMgr_GetBond(&bondInfo); GAP_Encrypt(connHandle, bondInfo.ltk); } ``` ### 常见错误码 | 错误码 | 含义 | 解决方法 | |-------|------|---------| | 0x05 | PIN/Key 缺失 | 检查配对码输入逻辑 | | 0x0E | 加密失败 | 验证双方安全参数兼容性 | | 0x16 | 不支持请求特性 | 检查从机安全能力 | ### 相关问题 1. **如何在CC2642上实现配对码的动态输入?** [^1] (如通过UART串口接收用户输入) 2. **安全连接与非安全连接如何共存?** (配置`GAPBOND_SECURE_ONLY`参数控制) 3. **如何验证安全连接的有效性?** (使用`GATT_ReadCharValue`尝试访问加密特征) 4. **LTK更新周期如何配置?** (通过`GAPBondMgr_SetParamValue(GAPBOND_LTK_SIZE)`) 5. **安全连接对功耗的影响如何量化?** [^2] (使用EnergyTrace工具测量ECDH执行能耗)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值