2. 固件版本管理模块实现
任务目标
实现IoT终端固件版本管理模块,包含版本号解析与比较、版本列表维护、版本状态跟踪,并支持版本回滚与灰度发布策略。本案例聚焦于终端侧版本管理核心逻辑,不涉及OTA传输协议与服务器端实现。
核心设计
版本号采用major.minor.patch三段式结构。版本状态机包含:ACTIVE(当前运行)、STANDBY(待回滚备份)、GRAY(灰度中)、ROLLBACK(回滚目标)。灰度发布通过维护一个灰度比例阈值,当终端标识哈希落入阈值区间时允许升级。
完整实现代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#define MAX_VERSION_LEN 32
#define MAX_VERSIONS 8
#define GRAY_THRESHOLD_DEFAULT 20 // 默认灰度比例20%
typedef enum {
VERSION_ACTIVE = 0,
VERSION_STANDBY,
VERSION_GRAY,
VERSION_ROLLBACK
} version_state_t;
typedef struct {
char version[MAX_VERSION_LEN]; // 版本号字符串
uint32_t major;
uint32_t minor;
uint32_t patch;
version_state_t state;
uint32_t gray_threshold; // 灰度阈值百分比 0-100
} firmware_version_t;
typedef struct {
firmware_version_t versions[MAX_VERSIONS];
int count;
char active_version[MAX_VERSION_LEN]; // 当前激活版本
} version_manager_t;
// 解析版本号字符串为三段数字
static int parse_version(const char* ver, uint32_t* maj, uint32_t* min, uint32_t* pat) {
if (sscanf(ver, "%u.%u.%u", maj, min, pat) != 3) return -1;
return 0;
}
// 比较两个版本号: a>b返回1, a==b返回0, a<b返回-1
static int compare_versions(const char* a, const char* b) {
uint32_t amaj, amin, apat, bmaj, bmin, bpat;
if (parse_version(a, &amaj, &amin, &apat) != 0) return -2;
if (parse_version(b, &bmaj, &bmin, &bpat) != 0) return -2;
if (amaj != bmaj) return amaj > bmaj ? 1 : -1;
if (amin != bmin) return amin > bmin ? 1 : -1;
if (apat != bpat) return apat > bpat ? 1 : -1;
return 0;
}
// 初始化版本管理器
void vm_init(version_manager_t* vm) {
memset(vm, 0, sizeof(version_manager_t));
vm->count = 0;
strcpy(vm->active_version, "0.0.0");
}
// 添加新版本到列表
int vm_add_version(version_manager_t* vm, const char* ver, uint32_t gray_threshold) {
if (vm->count >= MAX_VERSIONS) return -1;
if (parse_version(ver, NULL, NULL, NULL) != 0) return -2;
// 检查重复
for (int i = 0; i < vm->count; i++) {
if (strcmp(vm->versions[i].version, ver) == 0) return -3;
}
firmware_version_t* fv = &vm->versions[vm->count];
strncpy(fv->version, ver, MAX_VERSION_LEN - 1);
fv->version[MAX_VERSION_LEN - 1] = '\0';
parse_version(ver, &fv->major, &fv->minor, &fv->patch);
fv->state = VERSION_STANDBY;
fv->gray_threshold = gray_threshold;
vm->count++;
return 0;
}
// 激活指定版本(用于升级或回滚)
int vm_activate(version_manager_t* vm, const char* ver) {
for (int i = 0; i < vm->count; i++) {
if (strcmp(vm->versions[i].version, ver) == 0) {
// 将当前ACTIVE版本转为STANDBY
for (int j = 0; j < vm->count; j++) {
if (vm->versions[j].state == VERSION_ACTIVE) {
vm->versions[j].state = VERSION_STANDBY;
}
}
vm->versions[i].state = VERSION_ACTIVE;
strcpy(vm->active_version, ver);
return 0;
}
}
return -1; // 版本不存在
}
// 回滚到指定版本(自动将当前ACTIVE标记为ROLLBACK来源)
int vm_rollback(version_manager_t* vm, const char* target_ver) {
// 目标版本必须存在于列表中
int found = 0;
for (int i = 0; i < vm->count; i++) {
if (strcmp(vm->versions[i].version, target_ver) == 0) {
found = 1;
break;
}
}
if (!found) return -1;
// 执行回滚:将当前ACTIVE转为ROLLBACK状态,目标版本转为ACTIVE
for (int i = 0; i < vm->count; i++) {
if (vm->versions[i].state == VERSION_ACTIVE) {
vm->versions[i].state = VERSION_ROLLBACK;
}
}
return vm_activate(vm, target_ver);
}
// 灰度判断:根据设备ID哈希决定是否允许升级到指定版本
int vm_gray_check(version_manager_t* vm, const char* target_ver, uint32_t device_id) {
for (int i = 0; i < vm->count; i++) {
if (strcmp(vm->versions[i].version, target_ver) == 0) {
uint32_t hash = device_id * 2654435761u; // Knuth乘法哈希
uint32_t percent = hash % 100;
return percent < vm->versions[i].gray_threshold ? 1 : 0;
}
}
return -1; // 版本不存在
}
// 打印当前版本列表状态
void vm_print_status(version_manager_t* vm) {
printf("=== 固件版本列表 (共%d个版本) ===\n", vm->count);
printf("当前激活版本: %s\n\n", vm->active_version);
for (int i = 0; i < vm->count; i++) {
firmware_version_t* fv = &vm->versions[i];
const char* state_str;
switch (fv->state) {
case VERSION_ACTIVE: state_str = "ACTIVE"; break;
case VERSION_STANDBY: state_str = "STANDBY"; break;
case VERSION_GRAY: state_str = "GRAY"; break;
case VERSION_ROLLBACK: state_str = "ROLLBACK"; break;
default: state_str = "UNKNOWN"; break;
}
printf(" %-12s | %s | 灰度阈值: %u%%\n",
fv->version, state_str, fv->gray_threshold);
}
printf("===============================\n");
}
// 演示入口
int main() {
version_manager_t vm;
vm_init(&vm);
// 添加版本
vm_add_version(&vm, "1.0.0", 100); // 初始版本,全量
vm_add_version(&vm, "1.1.0", 20); // 灰度版本,20%设备
vm_add_version(&vm, "1.2.0", 100); // 正式版本
// 激活初始版本
vm_activate(&vm, "1.0.0");
vm_print_status(&vm);
// 模拟灰度发布:设备ID 1001尝试升级到1.1.0
printf("\n[灰度发布测试] 设备1001尝试升级到1.1.0...\n");
int allowed = vm_gray_check(&vm, "1.1.0", 1001);
printf("设备1001灰度判定: %s\n", allowed == 1 ? "允许升级" : "暂不升级");
// 模拟另一台设备
allowed = vm_gray_check(&vm, "1.1.0", 2002);
printf("设备2002灰度判定: %s\n", allowed == 1 ? "允许升级" : "暂不升级");
// 执行升级到1.1.0(假设灰度通过)
printf("\n[执行升级] 激活1.1.0...\n");
vm_activate(&vm, "1.1.0");
vm_print_status(&vm);
// 模拟回滚到1.0.0
printf("\n[回滚操作] 回滚到1.0.0...\n");
vm_rollback(&vm, "1.0.0");
vm_print_status(&vm);
return 0;
}
编译与运行
gcc -o version_mgr version_mgr.c -Wall
./version_mgr
验证方法与通过条件
程序执行后应依次输出:
- 初始状态:
1.0.0为ACTIVE,其余为STANDBY - 灰度判定:设备1001与2002的判定结果由哈希计算决定(可能一真一假或同真同假,取决于具体ID)
- 升级后:
1.1.0变为ACTIVE,原1.0.0转为STANDBY - 回滚后:
1.0.0重新变为ACTIVE,原1.1.0标记为ROLLBACK
通过条件:版本状态转换符合预期,灰度判定逻辑可复现(同一设备ID多次运行结果一致),回滚后原版本正确恢复为ACTIVE且旧版本状态被标记为ROLLBACK。
关键说明
- 灰度阈值
gray_threshold表示允许升级的设备百分比(0-100),在vm_add_version时设定 - 设备ID哈希采用Knuth乘法哈希,保证分布均匀且确定性
- 回滚操作将当前ACTIVE版本标记为
ROLLBACK状态,便于追踪回滚历史 - 实际工程中,
vm_activate成功后应触发固件搬运与重启切换,本案例仅管理版本状态