18.4 발행자(Publisher) 파이프라인의 C++ 소스 코드 심층 해부
지금까지의 장들(18.2~18.3절)을 통해 uORB 데이터가 담길 그릇(메모리 레이아웃)이 어떻게 빚어지고, 데이터가 유통될 OS 커널 수준의 파이프(VFS)가 어떻게 건설되는지 살펴보았다. 이제 설계 검토를 마치고 실제 비행 제어 모듈들의 C++ 소스 코드 최전선으로 뛰어들어 볼 차례다.
정보의 흐름을 만들어내는 첫 번째 주역은 단연코 발행자(Publisher) 이다. 센서 드라이버든, 자세 제어기든 자신이 생산한 데이터를 생태계 전체로 쏘아 올리려는 모듈은 반드시 uORB의 퍼블리셔 파이프라인을 통과해야만 한다.
1. 캡슐화(Encapsulation): C 함수에서 C++ 템플릿 클래스로의 진화
초기 PX4(혹은 레거시 코드) 시절에는 드라이버 작성 시 orb_advertise()와 orb_publish() 라는 순수 C 함수를 직접 호출해야 했다. 이 방식은 포인터 연산의 실수를 유발하기 쉽고, 무엇보다 컴파일러가 타입의 안전성(Type-safety)을 보장해 주지 못했다. (예를 들어, 자이로 토픽에 실수로 GPS 구조체 메모리를 밀어 넣어도 컴파일 에러가 나지 않고 비행 중에 기체가 추락했다).
이러한 참사를 막기 위해 현대의 PX4 소스 코드는 타입 안전성이 보장되는 C++ 템플릿 래퍼(Wrapper) 클래스인 uORB::Publication 과 uORB::PublicationMulti 를 전면적으로 도입했다.
// 1. 컴파일 타임에 타입(sensor_gyro_s)이 강제 지정되는 템플릿 객체 선언
uORB::Publication<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
// 2. 데이터 구조체 채우기
sensor_gyro_s gyro_data{};
gyro_data.timestamp = hrt_absolute_time();
gyro_data.x = 1.0f;
// 3. 단 한 줄의 메서드 호출로 발행 완료 (내부적으로 advertise와 publish를 모두 알아서 처리)
_sensor_gyro_pub.publish(gyro_data);
이 우아한 퍼블리셔 클래스는 생성과 소멸에 걸친 생명주기(Lifecycle) 관리의 부담을 개발자의 어깨에서 덜어주며(RAII 패턴), 오로지 “어떤 데이터를 쏘아 보낼 것인가“에 집중하게 만든다.
2. 퍼블리셔 파이프라인의 핵심 여정
이제 publish() 메서드가 호출되는 찰나의 순간에 지하 세계(Lower-level)에서 어떠한 소스 코드들이 폭포수처럼 연쇄 호출되는지 해부해 볼 것이다. 로직의 흐름은 크게 두 단계로 나뉜다.
- 지연 개설 (Lazy Advertisement): 센서 모듈이 너무 일찍 부팅되어 아직
/obj경로가 뚫려 있지 않은 경우, 데이터를 최초로 쏘려는publish()의 첫 호출 시점에 은밀하고 기민하게 하부 C 함수인orb_advertise()를 동기적으로 호출하여 토픽의 파이프를 먼저 뚫어낸다 (18.4.1절 상세 설명). - 데이터 밀어넣기 및 브로드캐스트: 파이프가 확보된 상태라면,
orb_publish()C 함수로 진입한 후, VFS 터널(file_operations의write)을 타고 들어가 최종 목적지인uORBDeviceNode의 링 버퍼에 구조체의 메모리를 O(1) 속도로 밀어 넣는다 (18.4.2절 상세 설명).
이어지는 하위 절들에서는 PX4-Autopilot의 실제 uORB 펌웨어 디렉토리 내부의 소스 코드를 해체하며, 이 두 가지 핵심 과정의 우아한 구현 기법들을 낱낱이 파헤칠 것이다.