21.7.3.1. uORB::Publication<T> 초기화 및 광고(Advertisement) 로직
구독자 모듈이 Subscription 래퍼로 안전하게 데이터를 읽어오듯, 데이터를 뿜어내는 센서 드라이버나 제어기 모듈들도 최신 C++에서는 원시 C API(orb_publish) 대신 uORB::Publication<T> 라는 템플릿 래퍼로 데이터를 쏘아 올린다.
이 래퍼 클래스는 출판을 담당하는 개발자가 실수로 메모리 덩어리를 통째로 날려먹거나, 엉뚱한 타입의 구조체를 잘못 뿌려서 시스템을 패닉에 빠뜨리는 일이 없도록 컴파일(Compile) 단계부터 강력한 족쇄(Type Enforcement)를 채워버린다.
1. C API의 “광고(Advertisement)“라는 절차
원시 C API 시절, 토픽을 세상에 내보내려면 반드시 **“광고(Advertisement, orb_advertise)”**라는 귀찮고 무서운 의식을 치러야 했다.
- 신고: “커널아, 나 지금부터
sensor_gyro라는 이름의 게시판을 하나 파서 글을 쓸 거야!” - 핸들 획득: 커널은 내부적으로 파일 디스크립터(File Descriptor) 테이블을 뒤져서 새로운 빈 슬롯을 파고, 그 번호(예:
fd = 5)를 반환해 준다. - 글쓰기: 이후부터는
orb_publish(ORB_ID(sensor_gyro), 5, &data)처럼 그 5번 핸들표를 들이밀며 데이터를 계속 덮어쓴다.
문제는 이 파일 디스크립터 관리 방식이 너무나도 취약하다는 것이다. 만약 개발자가 실수로 5번 핸들표를 잃어버리거나(메모리 릭), 센서가 재부팅되면서 핸들표를 닫아주지(orb_unadvertise) 않으면 어떻게 떨어질까?
운영체제의 좁은 파일 슬롯이 순식간에 말라붙어(FD Exhaustion) 시스템 전체가 마비되는 ENFILE (Too many open files) 패닉이 터지고 만다.
2. uORB::Publication<T>의 아키텍처 방어
PX4 코어팀은 이 원초적인 C API의 삽질을 우아하게 덮어버리기 위해 Publication<T> 클래스를 RAII(Resource Acquisition Is Initialization) 패턴으로 깎아 만들었다.
#include <uORB/Publication.hpp>
class CustomSensor {
public:
CustomSensor() {} // 생성자에서는 그저 래퍼 객체만 조용히 준비됨!
void Run() {
sensor_gyro_s gyro_data;
gyro_data.x = 10.0f;
// 데이터만 채워서 던지면 끝!
// 광고(Advertise)와 출판(Publish)이 이 한 줄 안에서 자동으로 해결된다!
_gyro_pub.publish(gyro_data);
}
private:
// 타입(sensor_gyro_s)을 템플릿으로 강제 시킨 출판자 래퍼
uORB::Publication<sensor_gyro_s> _gyro_pub{ORB_ID(sensor_gyro)};
};
내부 로직을 뜯어보면, _gyro_pub.publish(gyro_data)가 처음 호출되는 순간 래퍼가 알아서 “어? 나 아직 광고(Advertise)를 안 했네?” 하고 깨달은 뒤 내부적으로 orb_advertise()를 안전하게 호출하여 발급받은 orb_advert_t 핸들을 자기 뱃속 깊은 곳(Private member)에 숨겨둔다.
그리고 다음번 Run() 루프부터는 알아서 메모리 락 없이 빛의 속도로 그 핸들을 꺼내어 orb_publish()만 미친 듯이 쏘아대기 시작한다.
그리고 내 CustomSensor 모듈이 메모리에서 파괴(Destructor)될 때 C++ 소멸자가 자동으로 발동되어 orb_unadvertise()를 깔끔하게 처리해 주니, 메모리 릭(Leak)은 영원히 발생하지 않게 된다.
하지만 이토록 완벽해 보이는 이 초기화(Advertisement) 로직조차, 픽스호크의 심장부에서는 치명적인 ‘부팅 지연(Boot-up Latency)’ 덩어리로 전락할 수 있다. 그 무시무시한 운영체제 레벨의 파일 시스템 락(Lock)을 PX4가 어떻게 우회하는지, 극강의 최적화 트릭을 다음 장(21.7.3.1.1)에서 낱낱이 파헤쳐보자.