36940db938a8f4a1e919496793ed439850a221c2의 zbus를 기준으로 한다. Kernel Queues가 FIFO·msgq의 항목 저장 방식을 다룬다면, 여기서는 서비스들이 한 채널의 최신값과 게시 통지를 공유할 때 각 관찰자가 어떤 시점의 데이터를 보는지 설명한다. 그림과 예시는 설명용으로 새로 작성했다.1. 채널은 항목 목록이 아니라 하나의 현재 메시지다
ZBUS_CHAN_DEFINE은 메시지 타입·초기값·validator·관찰자 목록을 지정해 채널을 만든다. 채널에는 메시지 크기만큼의 저장 영역이 하나 있고, publish는 그 영역을 갱신한 뒤 관찰자 통지를 시도한다. 여러 송신자와 수신자는 채널 이름을 통해 느슨하게 연결되지만, 이것을 모든 과거 메시지가 쌓이는 로그로 읽으면 안 된다. 세 번째 경로인 message subscriber는 기본 꺼짐인 CONFIG_ZBUS_MSG_SUBSCRIBER=y를 설정해야 쓸 수 있다.
↔ 좁은 화면에서는 채널에서 세 관찰자로 이어지는 경로를 오른쪽으로 이동해 보세요.
const 메시지를 직접 읽는다.CONFIG_ZBUS_MSG_SUBSCRIBER=y일 때 게시 시점의 메시지를 net_buf에 담아 FIFO로 보낸다. wait_msg가 호출자 버퍼로 복사한다.근거: 채널의 메시지 참조·크기와 세 observer type, 채널 정의의 단일 메시지 저장 영역, message subscriber 설정 조건, 3.7.0 관찰자 설명.
2. publish는 복사와 통지를 같은 호출 안에서 진행한다
zbus_chan_pub()은 먼저 validator가 있다면 메시지를 검사한다. 채널 잠금을 얻은 뒤 송신자의 구조체를 채널 저장 영역으로 복사하고, VDED가 listener callback 호출·message subscriber 복사본 전달·subscriber 참조 통지를 관찰자 순서대로 수행한다. 그런 다음 잠금을 푼다. VDED는 별도 상주 스레드가 아니라 게시자의 실행 문맥에서 돌기 때문에, 느린 listener와 가득 찬 observer 큐는 게시 호출의 시간에 직접 영향을 준다.
↔ 좁은 화면에서는 validator부터 unlock까지 오른쪽으로 이동해 확인하세요. 베이지 단계에서 채널이 잠긴다.
거부하면 아직 채널 값은 바뀌지 않는다.
다른 read·publish·claim과 접근을 조정한다.
송신자의 스택 객체도 채널로 복사된다.
관찰자 목록의 순서대로 같은 게시 호출에서 통지한다.
오류가 있었어도 ③에서 갱신된 값은 남을 수 있다.
반환값과 채널 내용은 별개로 확인한다. Validator가 거부하면 복사 전에 -ENOMSG가 나온다. 반면 복사 뒤 observer 통지가 실패하면 publish가 오류를 반환해도 채널의 현재 메시지는 이미 새 값일 수 있다. 재시도를 무조건 “첫 게시가 전혀 없었다”는 전제로 설계하면 중복 통지나 상태 불일치가 생길 수 있다.
근거: validator·복사·통지·해제와 반환 순서, 세 observer 통지 함수, 게시자 문맥의 VDED 설명.
3. 게시 A 다음 B가 오고, 관찰자가 늦게 읽는다면
세 관찰자가 활성화되고 통지가 모두 성공한 상태에서, 한 채널에 A를 게시하고 곧 B를 게시한 뒤 비동기 관찰자 스레드가 실행되는 경우를 보자. Subscriber 큐에는 두 번의 채널 참조가 남을 수 있지만 두 참조 모두 같은 현재 채널을 가리킨다. 두 번의 zbus_chan_read()가 모두 B를 읽을 수 있다. Message subscriber는 게시 시점의 메시지를 net_buf에 담으므로 A와 B를 각각 회수할 수 있다. Listener는 A와 B의 VDED 시점마다 직접 호출된다.
↔ 좁은 화면에서는 A 게시, B 게시, 늦은 소비 순서로 오른쪽으로 이동해 보세요.
A로 교체B로 교체B가 현재값&channel 통지&channel 통지B 가능A, B 회수A 확인B 확인| 관찰자 | 전달 단위 | 메시지를 보는 때 | 비용·실패 경계 |
|---|---|---|---|
| Listener | 동기 callback과 잠긴 채널의 const 참조 | 각 publish/notify의 VDED 도중 | 느린 callback은 게시 호출을 늘림. 같은 채널을 다시 잠그는 read/claim은 피함 |
| Subscriber | msgq에 채널 포인터 | zbus_sub_wait() 후 별도 read 시점의 최신값 | 큐 깊이와 경쟁에 따른 통지 실패, 이전 값 덮어쓰기 |
Message subscriberCONFIG_ZBUS_MSG_SUBSCRIBER=y | net_buf에 담긴 게시 시점 메시지, FIFO | zbus_sub_wait_msg()가 호출자 버퍼로 복사 | 버퍼 풀과 메모리 비용. 풀 부족은 구성에 따라 assert·실패로 드러남 |
근거: callback·포인터 큐·net_buf clone, 게시 값의 버퍼 복사, 두 wait API의 회수 방식, 풀 한계와 최신값 덮어쓰기 경고.
4. read는 복사하고, claim은 잠금 수명을 호출자에게 넘긴다
zbus_chan_read()는 잠금을 얻고 채널 메시지 전체를 호출자의 버퍼에 복사한 다음 잠금을 푼다. zbus_chan_claim()은 잠금을 얻은 채 돌아와 직접 메시지나 user_data를 볼 수 있게 한다. 호출자는 반드시 zbus_chan_finish()로 잠금을 풀어야 한다. finish 자체는 관찰자에게 변경을 알리지 않는다. 직접 메시지를 수정했다면 필요한 통지는 finish 뒤 zbus_chan_notify()로 별도 수행한다.
| 호출 | 데이터 접근 | 잠금 종료 | 관찰자 통지 |
|---|---|---|---|
zbus_chan_pub | 송신자 버퍼 → 채널로 복사 | 호출 내부 | 예, VDED 실행 |
zbus_chan_read | 채널 → 호출자 버퍼로 복사 | 호출 내부 | 아니요 |
zbus_chan_claim | 직접 접근을 위한 잠금 획득 | 호출자가 finish | 아니요 |
zbus_chan_finish | claim한 채널 해제 | 이 호출에서 해제 | 아니요 |
zbus_chan_notify | 메시지 복사 없이 현재값 통지 | 호출 내부 | 예, VDED 실행 |
/* state_chan은 struct state_msg 채널로 미리 정의했다고 가정 */
int rc = zbus_chan_claim(&state_chan, K_MSEC(20));
if (rc == 0) {
struct state_msg *state = zbus_chan_msg(&state_chan);
state->level = next_level; /* 잠금 보유 중 직접 변경 */
zbus_chan_finish(&state_chan); /* 반드시 짝지어 해제 */
rc = zbus_chan_notify(&state_chan, K_MSEC(20));
/* rc를 확인: 통지가 실패해도 앞선 직접 변경은 남을 수 있다 */
}
finish와 뒤이은 notify는 하나의 원자적 작업이 아니다. 두 호출 사이에 다른 게시자가 채널 값을 바꾸면 notify는 바뀐 현재 값을 알릴 수 있다. 자신이 수정한 값의 게시·통지를 한 잠금 구간으로 묶어야 하면 zbus_chan_pub()의 계약을 사용한다.
Listener callback은 게시자의 잠금 구간 안에서 실행되므로 같은 채널을 다시 claim하거나 read하는 대신 zbus_chan_const_msg()로 현재 메시지를 직접 본다. 반대로 비동기 subscriber는 통지 후 채널이 아직 잠겨 있을 수 있으므로 read의 timeout을 여유 있게 잡고 실패를 처리한다.
근거: read·notify·claim·finish 실제 잠금 경로, claim/finish/notify 공개 계약, listener 접근과 claim 예시, subscriber read timeout 경고.
5. 통지의 순서와 처리 완료의 순서를 섞지 않는다
3.7.0의 VDED는 채널 정의에 나열한 관찰자, ZBUS_CHAN_ADD_OBS로 붙인 정적 관찰자, 실행 중 추가한 관찰자의 순서로 통지한다. 첫 묶음은 정의 순서, 두 번째 묶음은 매크로의 sequence priority, 마지막 묶음은 추가 순서다. 비활성 관찰자나 가려진 observation은 건너뛴다. 이것은 VDED가 통지를 시도하는 순서다. 깨어난 비동기 스레드가 언제 처리까지 마칠지는 우선순위와 선점에 달려 있다.
| 우선 통지 계층 | 등록 방법 | 계층 안의 순서 | 주의 |
|---|---|---|---|
| ① 채널 정의 관찰자 | ZBUS_CHAN_DEFINE(... ZBUS_OBSERVERS(...)) | 나열한 순서 | callback 실행과 큐 넣기가 게시 호출 안에서 진행 |
| ② 정의 후 정적 관찰자 | ZBUS_CHAN_ADD_OBS | 매크로의 sequence priority | 채널 정의 묶음보다 먼저 올 수 없음 |
| ③ 런타임 관찰자 | zbus_chan_add_obs() | 추가한 순서 | CONFIG_ZBUS_RUNTIME_OBSERVERS가 필요 |
통지 수신도 무조건 보장된다고 가정하지 않는다. Subscriber의 msgq가 가득 차면 통지 오류가 날 수 있고, message subscriber의 net_buf 확보도 실패할 수 있다. 후자는 설정에 따라 assert로 중단되거나 오류로 드러나므로 버퍼 풀 크기와 메모리 여유를 먼저 검증한다. Publish가 오류를 돌려주었다면 validator·잠금·관찰자 통지 중 어느 단계였는지 구분한다. CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE는 메시지 크기·생산 속도·소비 지연에 맞춰 정한다.
근거: 3.7.0의 세 통지 계층과 비활성 무시, 순서대로 통지하며 실패를 보존하는 구현, assert/mock 분기, 전달 한계와 풀 설정.
6. timeout은 채널 잠금뿐 아니라 게시 통지에도 걸린다
timeout은 validator나 listener callback의 실행을 강제로 중단하지 않는다. 따라서 호출 전체의 실행 시간 상한으로 읽으면 안 된다. 게시 중 계산한 남은 시간은 뒤이은 큐·버퍼 대기에 전달되며, callback 실행 비용은 별도로 제한해야 한다.
| API | timeout이 걸리는 경로 | ISR에서의 경계 | 실패 뒤 확인 |
|---|---|---|---|
pub / notify | 채널 잠금과 VDED의 큐·버퍼 전달에 남은 시간 사용 | K_NO_WAIT만 사용. Listener도 ISR 문맥에서 실행될 수 있음 | validator 이전 거부인지, 복사 뒤 통지 실패인지 구분 |
read / claim | 채널 잠금 획득 | K_NO_WAIT만 사용 | 바쁜 채널·시간 초과를 처리; claim 성공이면 finish 필수 |
finish | 대기 없이 잠금 해제 | 성공한 claim과 같은 제어 흐름에서 호출 | finish가 알림을 보내지는 않음 |
sub_wait / sub_wait_msg | 관찰자 큐/FIFO에서 기다림 | ISR 호출 금지; 스레드에서 처리 | 통지만 받았는지 메시지 복사본을 받았는지 구분 |
K_NO_WAIT는 “CPU 작업이 0”이라는 뜻이 아니다. ISR에서 publish하면 잠금을 기다리지는 않지만 VDED의 listener 호출과 메시지 복사·통지 시도는 여전히 ISR 문맥에서 실행된다. 긴 callback이나 큰 메시지 전달을 ISR 경로에 두지 않는다.
근거: ISR K_NO_WAIT 강제와 pub·read·claim 경로, 두 구독 대기의 ISR 금지, 게시자 문맥의 VDED, 공개 timeout·finish 계약.
7. 최신 상태, 모든 샘플, 즉시 반응 중 무엇이 필요한가
| 필요한 것 | 가까운 선택 | 확인할 경계 |
|---|---|---|
| 게시 중 짧은 검증·반응 | Listener | callback이 publisher와 같은 문맥·잠금 안에서 끝나는가 |
| 채널의 최신 상태만 읽으면 됨 | Subscriber + chan_read | 참조 통지 두 건이 과거값 두 건을 보장하지 않음 |
| 각 게시의 메시지 내용을 나중에 처리 | Message subscriberCONFIG_ZBUS_MSG_SUBSCRIBER=y 필요 | net_buf 풀·전달 오류·소비 속도 |
| 대량 바이트 스트림 | Pipe 등 데이터 경로 재검토 | zbus의 채널 잠금·복사·VDED 비용 |
진단할 때는 publish 호출의 반환값만 남기지 말고 채널 이름, validator 결과, 잠금 대기, 관찰자별 통지 결과, subscriber 큐 깊이와 message subscriber 버퍼 풀 상태를 나란히 기록한다. 과거 샘플이 필요했는데 최신값만 보였다면 subscriber가 채널 참조를 받은 시점과 실제 read 시점을 먼저 비교한다.
8. 고정 버전 근거와 이어 읽기
기준 문서는 Zephyr 3.7.0 zbus다. 구현은 같은 릴리스 commit의 zbus.c, 공개 타입과 API는 zbus.h에 고정했다. 위 각 절의 좁은 출처 링크는 실제 복사·잠금·통지 위치를 가리킨다.