Zephyr v3.7.0 · Interprocessor Communication

IPC Service: 두 이미지 사이에서 endpoint는 언제 준비될까?

한 이미지의 k_msgq에 값을 넣는 것과 다른 도메인 또는 CPU의 이미지에 메시지를 건네는 것은 경계가 다릅니다. IPC Service에서는 instance를 열고 endpoint를 등록한 다음, 양쪽의 결합 완료와 backend의 버퍼 수명을 따로 확인해야 합니다.

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

기준은 Zephyr v3.7.0, commit 36940db938a8f4a1e919496793ed439850a221c2다. 이 글의 IPC는 서로 다른 도메인 또는 CPU 사이의 서비스 API를 뜻한다. 기존 IPC / Synchronization 글의 semaphore·mutex·k_msgq는 같은 커널 이미지 안의 객체다. 여기의 도식은 Zephyr 원본 그림을 복사하지 않고 소스의 책임 경계를 다시 그렸다.

1. instance는 물리적 길, endpoint는 애플리케이션의 연결점

ipc_service_open_instance()가 받는 device는 backend가 만든 통신 instance다. instance의 물리 경로와 내부 구조는 backend에 달려 있다. 애플리케이션은 그 instance에 ipc_service_register_endpoint()로 endpoint를 등록하고, 등록된 struct ipc_ept로 송신한다. 두 이미지의 객체 주소를 공유한다는 뜻이 아니다. 반대로 k_msgq는 한 Zephyr 커널의 큐 객체에 고정 크기 항목을 복사한다.

그림 1. ICMsg 예시로 본 각 이미지의 endpoint와 실제 전송 경계

↔ 좁은 화면에서는 왼쪽 이미지에서 오른쪽 이미지까지 가로로 이동해 보세요.

이미지 A · 로컬 도메인

애플리케이션 Aipc_ept로 send, received callback으로 수신
IPC Service instance + endpoint등록된 endpoint를 backend token에 연결
ICMsg 예시: TX region + TX MBOXpayload는 공유 RAM, 알림은 MBOX 신호
payload
공유 RAM ↔
알림
MBOX ↔

이미지 B · 원격 도메인

애플리케이션 B대응 endpoint의 callback과 응답 송신
IPC Service instance + endpoint자체 device, 자체 endpoint 객체
ICMsg 예시: RX region + RX MBOXA의 TX에 대응하는 영역·채널을 B에서는 RX로 구성
표 1. 커널 큐와 이미지 간 IPC의 전달 범위
질문k_msgqIPC Service endpoint
어디를 연결하나?같은 커널 이미지의 thread·ISR두 도메인/CPU의 각 이미지
호출자가 쥐는 객체커널 큐 객체 주소instance device와 등록된 endpoint
전송의 구현큐의 고정 크기 항목 복사선택한 backend의 전송·알림 방식
수신 준비의 신호큐 초기화와 항목 유무양쪽 endpoint 결합을 알리는 bound callback

근거: IPC Service instance·endpoint 정의, v3.7.0 k_msgq 객체와 복사, ICMsg 공유 메모리·MBOX.

2. 등록에 성공했다고 원격 결합이 완료된 것은 아니다

애플리케이션은 instance를 먼저 연 뒤 endpoint 설정과 객체를 등록한다. ipc_ept_cfg가 가리키는 설정은 endpoint를 사용하는 동안 살아 있어야 한다. register_endpoint()의 반환 0은 로컬 등록 성공이며, 원격 endpoint까지 결합됐다는 확인은 bound callback에서 온다. 그래서 callback은 결합 신호를 기록하고, 실제 송신 흐름에서는 첫 send의 반환값까지 확인한다.

그림 2. 로컬 등록, 원격 결합, 첫 송신의 서로 다른 시점

↔ 좁은 화면에서는 ①부터 ⑤까지 오른쪽으로 이동하세요. 주황 칸이 결합 경계입니다.

① instance 선택DEVICE_DT_GET(DT_NODELABEL(ipc0))

device와 backend 구성이 이 보드 이미지에 있는지 확인

② 열고 등록open_instance → register_endpoint

각 반환값 확인. 설정 객체의 수명 유지

③ 원격 준비peer endpoint + transport

반대쪽 이미지도 같은 통신 관계로 준비

④ 결합 통지cfg.cb.bound(priv)

callback은 결합 신호 기록. 깨어난 thread의 첫 송신도 결과 확인

⑤ 데이터 왕복send → received

바이트 수 또는 음수 오류와 수신 버퍼 수명 확인

v3.7.0 ICMsg의 작은 순서 차이. ICMsg 라이브러리는 결합 패킷을 읽은 뒤 bound callback을 호출하고 이어서 내부 상태를 READY로 바꾼다. callback 본문이나 그 신호로 곧바로 깨어난 thread가 상태 변경보다 먼저 ipc_service_send()를 실행하면 -EBUSY가 나올 수 있다. callback 안에서 바로 송신하지 말고 별도 실행 흐름에서 첫 송신 오류를 검사해 제한된 재시도·오류 처리를 둔다.

근거: endpoint 설정 수명·API, open·register 위임, ICMsg callback과 READY 설정 순서, READY 전 송신 결과.

3. 한 API라도 backend가 endpoint 수와 기능을 정한다

IPC Service는 앱을 backend 세부 구현에서 떼어 놓는 인터페이스지만 기능을 자동으로 같게 만들지는 않는다. v3.7.0의 ICMsg는 인스턴스마다 endpoint 하나를 열고 공유 RAM의 packet buffer와 MBOX를 쓴다. ICBMsg는 ICMsg 위의 블록 할당을 사용해 여러 endpoint와 no-copy 버퍼 API를 제공한다. 양쪽 이미지는 각자의 TX/RX region과 MBOX 방향을 맞춰야 한다.

표 2. ICMsg·ICBMsg backend 계약
v3.7.0 backendendpoint전송 구성이 글에서 주의할 API
ICMsginstance당 1개공유 RAM packet buffer + MBOXsend는 있으나 no-copy hook 없음. 추가 채널은 별도 instance 필요
ICBMsg여러 개, 설정된 개수 한도공유 RAM 블록 + ICMsg 제어 경로 + MBOXget_tx_buffer·send_nocopy·RX hold/release hook 제공. 이 버전의 backend에는 close/deregister hook 없음

공통 wrapper는 해당 backend의 함수 포인터로 위임한다. 예컨대 v3.7.0의 ipc_service_get_tx_buffer()는 backend에 관련 hook이 없으면 -EIO를 반환한다. API 문서의 선택적 no-copy 설명만 보고 ICMsg에서 no-copy가 가능하다고 판단할 수 없다. ICBMsg의 여러 endpoint도 무한 개가 아니며 메모리와 설정 한도를 따른다.

근거: ICMsg 1 endpoint·region/MBOX 구성, ICBMsg 다중 endpoint·블록 할당, ICMsg backend ops, ICBMsg backend ops, no-copy hook 검사.

4. received 포인터와 no-copy TX 포인터의 소유권을 분리한다

기본 ipc_service_send()는 호출자 버퍼와 길이를 backend에 넘기고 전송 결과로 보낸 바이트 수 또는 음수 오류를 받는다. no-copy 지원 backend에서는 get_tx_buffer()로 얻은 TX 공간을 채우고 send_nocopy()로 넘긴다. 성공적으로 넘긴 뒤 송신자는 그 포인터를 더는 만지면 안 된다. 전송하지 않는 버퍼는 drop_tx_buffer()로 반납한다. 실패하면 API 계약과 backend의 실제 반환을 확인한 뒤 반납 경로를 처리한다. 수신 포인터를 callback 뒤에도 직접 쓰려면 received callback이 반환되기 전에 지원 backend의 hold_rx_buffer()가 성공해야 한다.

그림 3. 두 방향의 버퍼 소유권 시간선

↔ 좁은 화면에서는 왼쪽의 확보부터 오른쪽의 해제까지 이동하세요.

방향① 확보② 사용하는 동안③ 넘기거나 보유④ 최종 해제
no-copy TX
지원 backend만
get_tx_buffer()
빈 전송 버퍼 획득
요청·실제 크기 확인 뒤 payload 작성
send_nocopy() 성공 후 송신자 접근 중지
미전송 버퍼는 drop_tx_buffer(). 송신 오류 때는 backend의 해제 상태 확인
RX callback
수신 측
received(data,len)
backend가 제공
callback 안에서 소비하거나 자체 버퍼로 복사
callback 반환 전 hold_rx_buffer() 성공해야 밖에서 직접 사용 가능
보유한 버퍼만 release_rx_buffer(); 보유하지 않았다면 callback 반환 후 사용 금지
표 3. 송신·수신 버퍼의 소유권
경로안전한 기본 규칙특히 피할 오해
일반 send전송 결과를 바이트 수와 음수 오류로 검사반환 0을 모든 데이터 전송 성공으로 취급
no-copy TX지원 여부, 획득 크기, backend별 반환과 성공 뒤 소유권 이전을 확인반환 양수를 항상 payload 길이로 해석하거나, 보낸 뒤 같은 포인터를 수정
receivedcallback 안에서 소비·복사; 밖에서 직접 쓰려면 callback 반환 전 hold 성공 후 releasecallback의 data를 영구 버퍼처럼 저장

근거: v3.7.0 no-copy 사용 예시, TX 확보·전송·반납·RX hold/release 계약, received callback 버퍼 수명, ICBMsg 버퍼 hook.

ICBMsg v3.7.0의 no-copy 반환은 별도로 읽는다. 일반 send()는 성공 시 payload 길이를 반환하지만, 이 버전의 send_nocopy()는 send_block()을 거쳐 내부 ICMsg 제어 메시지의 송신 결과를 그대로 반환한다. 따라서 양수 반환을 payload 길이와 비교해 전송 실패로 처리하면 안 된다. 또한 일부 오류 경로는 TX 블록을 이미 해제하므로 오류마다 무조건 다시 drop_tx_buffer()를 호출하지 않는다. 이는 공통 API 문구만으로 알 수 없는 고정 backend의 예외다. 근거: 제어 메시지 반환·블록 해제, no-copy 위임.

5. 실패는 등록, 결합, 전송, 버퍼 단계에서 따로 관찰한다

하나의 “IPC 실패”로 뭉뚱그리면 조치가 달라진다. 로컬 등록 반환과 원격 결합 신호, 송신 반환, 버퍼 할당 결과를 순서대로 기록한다. 아래 오류는 가능한 예시이며 backend가 더 구체적인 errno를 돌려줄 수 있다.

표 4. 등록·결합·전송 실패의 확인 지점
관찰 지점가능한 신호바로 확인할 것
instance open-EINVAL, -EIO, backend별 errnoDT node·device 준비·backend 연결
endpoint register-EINVAL, -EBUSY, backend별 errnocfg/object 수명과 instance·endpoint 수 한도
결합 대기등록은 0이나 bound 미도착원격 이미지 기동, region과 MBOX 방향, 양쪽 endpoint 구성
send-ENOENT, -EBUSY, -EBADMSG, 메모리/버퍼 오류등록 상태, 결합 시점, 길이와 가용 공간; 실제 backend errno
no-copy API지원 hook 부재 또는 버퍼 부족선택 backend ops, 요청 길이, 획득·반납 균형

반환값은 전송 보증서가 아니다. ipc_service_send()의 양수 바이트 수는 로컬 backend가 그만큼 송신 처리했음을 나타낸다. 원격 애플리케이션이 처리했는지까지 확인하려면 상위 프로토콜에서 응답·타임아웃·재시도 정책을 별도로 설계해야 한다. 이 부분은 서비스 API의 제공 범위를 바탕으로 한 설계상 추론이다.

근거: open·register·send 반환 계약, backend send 위임, ICMsg READY·길이·MBOX 오류.

6. 실제 보드 검증은 두 이미지의 배치도부터 시작한다

서로 다른 보드·SoC에 모든 backend가 똑같이 붙는다고 가정할 수 없다. 예를 들어 ICMsg라면 두 이미지의 tx-region과 rx-region이 반대로 대응하고, 공유 가능한 물리 RAM과 송수신 MBOX 채널이 맞아야 한다. cache를 쓰는 쪽이 있다면 양쪽의 정렬·cache 운용도 함께 확인한다. 애플리케이션 로그는 등록 성공과 결합 완료를 별도 timestamp로 남긴다.

① 빌드 산출물

각 이미지의 zephyr.dts·.config에서 backend compatible, 두 region, MBOX, endpoint 수 설정을 대조한다.

② 연결 시간

양쪽 open/register 반환, bound callback, 첫 send의 시각과 반환값을 따로 기록한다.

③ payload 경계

최대 길이 근처와 빈 메시지, 버퍼 부족을 넣어 backend별 errno와 원격 수신 길이를 확인한다.

④ 버퍼 수명

callback 밖으로 넘기는 payload는 복사하거나 지원 backend에서 hold/release 성공을 추적한다.

이 글은 v3.7.0 API·backend 소스를 읽어 만든 설계 지도다. 특정 보드의 양쪽 이미지를 빌드하거나 실제 MBOX·공유 RAM 전송을 수행한 측정 결과는 포함하지 않는다.

근거: ICMsg 양쪽 region/MBOX 구성, ICBMsg region·block 구성.

7. 고정 버전 근거와 이어 읽기

문서와 구현 기준을 모두 Zephyr v3.7.0에 고정했다. 최신 개발판의 backend 기능·callback 구성·오류 반환을 이 글에 소급하지 않았다.

표 5. IPC Service 고정 버전 근거의 역할
고정 근거이 글에서 확인한 범위
IPC Service 공식 문서instance/endpoint 정의, 등록 순서, buffer API 계약
ipc_service.h · ipc_service.ccallback 수명과 wrapper의 backend 위임·hook 검사
ipc_icmsg.c · icmsg.cendpoint 한 개, READY 설정 순서, 송신 errno
ipc_icbmsg.c · ICBMsg 문서다중 endpoint·블록·no-copy hook