Zephyr v3.7.0 · 영속 설정

설정 키는 어떻게 재부팅을 건너는가

settings_save_one()의 문자열 키와 NVS의 플래시 ID는 같은 계층이 아닙니다. Settings는 모듈별 설정의 이름과 복원 시점을 다루고, NVS는 선택 가능한 저장 backend 중 하나로서 실제 플래시 기록을 관리합니다.

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

이 글은 Zephyr v3.7.0의 공식 문서와 tag commit 36940db938a8f4a1e919496793ed439850a221c2를 기준으로 한다. Settings API와 NVS backend의 책임 경계만 다룬다. 파일시스템 내부, 암호화 저장, 보드별 플래시 수명 수치와 최신 개발판의 ZMS 선택은 범위 밖이다. 그림은 설명을 위해 새로 그렸다.

1. Settings는 이름을 알고, NVS는 저장 공간을 안다

애플리케이션은 app/mode처럼 하위 트리로 나뉜 문자열 키를 Settings에 건넨다. 모듈 handler는 복원한 값을 RAM 상태에 반영하고, backend는 영속 매체에서 읽고 쓴다. 직접 저장인 settings_save_one()은 handler를 거치지 않으며, 전체 저장인 settings_save()는 handler의 h_export를 이용한다. 3.7.0 Settings는 FCB·파일·NVS backend를 제공한다. NVS 자체 API는 16비트 숫자 ID와 데이터를 플래시에 기록한다. 고정 버전의 Settings NVS adapter는 한 설정의 이름과 값을 별도 NVS entry로 관리한다. 따라서 Settings의 키 이름과 NVS의 ID를 동일한 공개 API로 혼동하지 않는다.

그림 1. Settings와 NVS의 책임, 직접 저장·전체 저장·복원 경로

↔ 좁은 화면에서는 네 책임 칸과 아래 세 경로를 가로로 이동해 보세요.

위 네 칸은 API, handler, NVS backend, flash의 책임. 아래 세 줄은 직접 저장은 handler를 거치지 않고 backend로, 전체 저장은 h_export를 거쳐 backend로, 복원은 NVS source에서 h_set과 선택적 h_commit을 거쳐 RAM으로 가는 경로
표 1. Settings와 NVS의 층별 책임
계층다루는 이름·단위책임오해하기 쉬운 점
애플리케이션·Settings APIapp/mode 같은 문자열 키로드·저장 요청API 호출만으로 RAM 값이 자동 변경된다고 단정하지 않음
모듈 handler등록한 subtree와 값h_set·h_commit·h_export 계약값 복원과 실제 기능 적용 시점을 구분
Settings backend직렬화한 키·값읽기 source와 쓰기 destination 연결source는 여러 개 가능해도 destination은 하나
NVS·flash16비트 ID·데이터, sector기록·복구·sector 재사용NVS의 ID는 애플리케이션의 문자열 키가 아님

근거: Settings 개요·backend·handler, NVS ID·데이터·sector, 이름·값을 별도 NVS entry로 읽는 3.7.0 adapter.

2. 로드는 읽기, handler 호출, 적용의 순서로 본다

애플리케이션이 시작할 때 Settings 초기화와 해당 subtree handler 준비가 끝난 뒤 settings_load()를 호출한다. 동적 handler라면 CONFIG_SETTINGS_DYNAMIC_HANDLERS와 등록을 확인하고, 정적 handler는 정의 macro로 포함한다. 등록된 영속 source에서 각 값을 읽으면 맞는 handler의 h_set이 RAM 상태를 구성한다. 전체 로드 뒤 h_commit을 사용하는 handler는 그 시점에 서로 의존하는 값들을 함께 적용할 수 있다. h_set과 h_commit이 모든 모듈에서 반드시 같은 일을 한다는 뜻은 아니다.

그림 2. 재부팅 뒤 설정 복원의 호출 경계

↔ 좁은 화면에서는 시간 흐름을 오른쪽으로 이동해 확인하세요.

초기화와 handler 등록 후 settings_load가 source를 읽어 h_set에 값을 전달하고 전체 로드 후 h_commit이 적용하는 순서
표 2. handler 콜백의 시점과 역할
콜백대표 호출 계기모듈이 할 일주의
h_set영속 값을 settings_load()로 복원이름·길이를 확인하고 값을 읽어 RAM 상태 구성불완전한 조합을 곧바로 외부에 적용할 필요 없음
h_commit전체 로드 뒤필요하면 여러 값의 조합을 최종 적용handler에 정의하지 않았다면 이 단계의 작업도 없음
h_exportsettings_save()의 전체 저장현재 RAM 값을 저장 함수에 넘김settings_save_one()의 직접 저장과 경로가 다름

3.7.0 구현의 반환값 경계. settings_load_subtree()는 source별 csi_load의 반환값을 모으지 않고 마지막 settings_commit_subtree() 결과를 반환한다. handler의 h_set 오류도 로그를 남긴 뒤 무시하는 경로가 있다. 따라서 settings_load() == 0만으로 모든 설정 키의 복원 성공을 인증하지 않는다. 필요한 키의 실제 RAM 값과 오류 로그를 함께 확인한다.

근거: 3.7.0 handler 계약, 로드 절차, settings_load_subtree의 반환, h_set 오류 처리, 공식 Settings 샘플.

3. 한 키를 바로 저장할지, handler 상태를 내보낼지 선택한다

settings_save_one("app/mode", &mode, sizeof(mode))은 지정한 키와 값을 destination backend에 직접 저장한다. 반대로 settings_save()는 등록된 handler의 h_export를 통해 현재 상태를 내보낸다. save_one()으로 저장한 키가 자동으로 h_export의 대상이 되지는 않는다. 전체 저장에 포함하려면 handler 쪽 export 계약을 구현해야 한다. 두 호출 모두 오류 반환을 확인하고, 런타임 RAM 상태 갱신은 애플리케이션의 흐름에서 명시한다.

표 3. save_one과 save의 값 출처
호출값의 출처handler 경계적합한 상황
settings_save_one(name, value, len)호출자가 넘긴 단일 값전체 h_export 순회가 필요하지 않음한 설정을 바꿀 때 바로 영속화
settings_save()각 handler가 내보내는 현재 상태h_export가 포함한 키를 저장모듈 상태들을 한 번에 내보낼 때
settings_load()등록된 source에 이미 저장된 값h_set 후 필요하면 h_commit부팅·재초기화할 때 RAM 상태 복원
/* 호출 순서 예시. 동적 handler 옵션·backend·보드 설정은 별도 필요. */
int rc = settings_subsys_init();
if (rc == 0) { rc = settings_register(&app_handler); }
if (rc == 0) { rc = settings_load(); }
if (rc == 0) {
    /* 3.7.0에서 rc == 0만으로 모든 키의 복원은 입증되지 않는다. */
    /* 필요한 mode의 복원 여부를 앱 상태와 로그로 검증한다. */
    mode = new_mode;  /* 이 예시의 RAM 상태 갱신 */
    rc = settings_save_one("app/mode", &mode, sizeof(mode));
}
/* rc를 확인하고, 오류 시 복구 정책은 애플리케이션이 결정한다. */

예시를 완성된 보드 코드로 복사하지 않는다. CONFIG_SETTINGS_DYNAMIC_HANDLERS, app_handler의 name·h_set 구현, backend Kconfig, 저장 partition과 복원값 검증이 필요하다. 초기화·등록·쓰기 오류와 복원 누락을 분리해서 다룬다.

근거: 3.7.0 save 경로, Settings API 계약.

4. NVS는 먼저 데이터를 쓰고, 나중에 기록 정보를 쓴다

NVS는 플래시 공간을 여러 sector로 나누고 ID·데이터 쌍을 끝에 덧붙인다. 데이터는 sector 앞쪽에서, 해당 기록을 가리키는 metadata는 뒤쪽에서 자란다. 데이터를 먼저, metadata를 나중에 기록하므로 전원 차단으로 metadata가 빠진 데이터는 재시작할 때 유효한 항목으로 취급하지 않는다. 같은 ID·데이터를 다시 쓰려 하면 불필요한 플래시 쓰기를 건너뛴다. 공간이 차면 유효한 최신 항목을 보존하며 sector를 재사용한다.

그림 3. NVS sector 안의 데이터·metadata와 불완전 기록

↔ 좁은 화면에서는 왼쪽 기록과 오른쪽 복구 조건을 모두 확인하세요.

NVS sector 앞쪽 데이터, 뒤쪽 metadata. 데이터만 기록되고 metadata가 없으면 유효 항목이 아니며 최신 ID 데이터 보존 후 sector 재사용

NVS metadata의 CRC는 기록 완료를 살피는 데 사용한다. 값 데이터의 무결성 검사가 항상 켜져 있다는 의미는 아니다. 3.7.0 문서는 데이터 CRC-32를 CONFIG_NVS_DATA_CRC로 별도 활성화하며, 기존에 CRC 없이 기록한 공간에서 이 옵션을 켜면 그 기존 데이터를 무효로 읽게 된다고 설명한다. 실제 제품에서 옵션을 바꾸기 전에는 마이그레이션과 보존 정책을 점검해야 한다.

표 4. backend 선택과 NVS 기록 특성
선택·조건3.7.0에서 하는 일검토할 위험
Settings FCB / File / NVS같은 Settings API 아래 서로 다른 영속 저장backend를 바꿔도 기존 형식이 자동 이관되지는 않음
Settings read source·write destinationsource 여러 개, destination 하나로드 우선순위·실제 쓰기 대상 확인
NVS metadata CRCmetadata 기록의 유효성 확인데이터 payload CRC와 동일시하지 않음
CONFIG_NVS_DATA_CRC전체 데이터 읽기에 선택적 CRC-32 검사기존 비CRC 공간과 호환성 문제
sector 여유 공간최신 ID·데이터 보존과 sector 재사용빈 공간이 부족하면 쓰기 실패 가능

근거: 3.7.0 NVS 기록·CRC·sector 문서, Settings backend 선택.

5. 저장 실패를 API 오류와 플래시 조건으로 나누어 본다

설정이 재부팅 후 사라졌다면 먼저 handler가 해당 키를 받는지, 복원된 RAM 값과 로그를 확인한다. settings_load()의 0 반환만으로 source별 읽기와 h_set 성공을 모두 결론 내리지 않는다. 다음으로 실제 빌드의 backend 선택과 partition, 쓰기 호출 반환값을 확인한다. 3.7.0의 NVS backend는 기본적으로 storage 레이블 고정 partition을 사용하고, devicetree의 zephyr,settings-partition chosen 속성으로 다른 partition을 고를 수 있다. 이름만 맞아도 플래시 장치·크기·쓰기 조건이 맞지 않으면 영속성을 보장할 수 없다.

표 5. 설정 유실·플래시 오류의 점검 순서
관찰먼저 확인다음 분기
부팅 후 기본값만 남음handler 준비 시점, RAM 값·오류 로그load() 0이어도 키 이름·h_set·source 확인
직접 저장은 성공, 전체 저장 뒤 값 누락h_export에 그 키를 포함했는가save_one()과 save()의 경로 구분
쓰기 오류·공간 부족backend destination·partition·플래시 상태NVS sector 크기·개수·여유 공간
CRC 옵션 변경 뒤 옛 값 소실기록 시점의 CONFIG_NVS_DATA_CRC기존 데이터 형식의 이관·복구 계획
민감 설정 저장선택한 backend와 위협 모델Settings API 자체를 암호화 저장으로 간주하지 않음

이 표는 원인 분리 순서다. 특정 보드에서 플래시 수명, 정전 복구 또는 보안 성질이 충족됐다는 시험 결과가 아니다. NVS 쓰기 수명은 실제 erase 내구도, sector 크기와 갱신 빈도로 따로 계산해야 한다.

6. 고정 버전 자료와 이어 읽기

API·handler·backend·partition의 기준은 Zephyr 3.7.0 Settings 문서, NVS ID·sector·CRC의 기준은 Zephyr 3.7.0 NVS 문서다. 예시 흐름은 공식 Settings API 샘플과 비교할 수 있다. 버전 고정 구현 출발점은 settings_nvs.c, settings_store.c, settings.c와 settings.h다.