21.3. C/C++ 기반 최소 단위 모듈(Hello World) 소스 코드 해부
건축가가 설계도(Kconfig)를 그리고 시공사(CMake)와 계약을 마쳤다면, 이제 드디어 벽돌(C++ 코드)을 쌓아 올릴 시간이다.
앞선 21.1 단원과 21.2 단원의 길고 지루한 아키텍처 및 빌드 시스템 이론을 묵묵히 통과해 온 여러분은 이제 PX4의 거대한 심장 박동(Run-loop) 위에서 살아 숨 쉴 수 있는 완벽한 자격을 갖추었다.
이 단원에서는 세상에서 가장 단순하지만, PX4 펌웨어의 모든 핵심 생태계를 완벽하게 탑재하고 있는 ’최소 단위 모듈(Hello World)’의 척추 구조를 C++ 레벨에서 한 줄 한 줄 현미경으로 해부해 본다.
1. ’순수 C++ 데스크톱 앱’과의 결정적 차이
리눅스나 윈도우에서 작동하는 데스크톱용 C++ 프로그램을 짜봤던 백엔드 개발자라면, 으레 텅 빈 파일에 #include <iostream>과 int main(int argc, char *argv[])을 적고 괄호를 여는 것으로 코딩을 시작할 것이다.
하지만 픽스호크에 올라가는 PX4 모듈 개발에서 저런 순수 C++ 스타일의 main 함수 작성법은 통하지 않는다. 심지어 std::cout 객체를 사용하여 콘솔에 글자를 찍으려 했다간 펌웨어 링커가 “해당 라이브러리를 찾을 수 없다“며 자비 없이 오류를 뿜어댈 것이다. 왜냐하면 우리는 무거운 표준 입출력 스트림(Standard I/O) 라이브러리를 감당할 수 없는, 극도로 다이어트된 256KB 램의 임베디드 RTOS(NuttX) 세상에 들어왔기 때문이다.
PX4 커스텀 모듈의 소스 코드는 데스크톱 앱과는 달리 다음과 같은 독특한 구조적 특징을 강제받는다.
- C와 C++의 기묘한 동거:
가장 바깥쪽 껍데기(엔트리 포인트)는 운영체제(NuttX) 커널이 C 언어 기반으로 호출할 수 있도록 반드시 **C 링키지(Linkage)**를 가져야 한다. 하지만 그 안에서 돌아가는 알맹이 제어 로직은 객체지향적인 C++ 클래스로 짜여야 한다 (21.3.1 단원에서 상세히 다룸). - 무한 상태 머신(State Machine)의 보장:
여러분은 함수가 메모리에 한 번 적재된 후 무한히 런루프(Run-loop)를 돌며 입력을 기다리는 무한 상태 머신을 만들어야 한다. 1회성으로 계산하고 끝나는 스크립트 방식의 코딩은 PX4에서 쓸모가 없다. - px4_platform API 체계 복종:
printf대신PX4_INFO매크로를 써야 하고,sleep대신px4_usleep을 써야 하며,new나malloc사용은 극도로 자제된다. PX4가 래핑(Wrapping) 해둔 권장 OS API들을 달달 외워야만 생존할 수 있다.
2. 최소 단위 모듈의 3단 해부도
여러분이 앞으로 만들게 될 모든 PX4 커스텀 앱(또는 모듈) 소스 파일(custom_app.cpp)은 뼈대가 모두 똑같이 생겼다. 크게 세 가지 덩어리(Section)로 나눌 수 있다.
- 엔트리 포인트 래퍼 (Entry Point Wrapper):
NSH 콘솔 터미널에서 사용자가custom_app start를 쳤을 때, 터미널 텍스트를 파싱하여 우리 모듈 클래스의 멱살을 잡고 깨워 메모리에 안착시키는 ‘관문’ 역할을 한다. - 클래스 설계 (Class Definition):
스레드 안전성(Thread-safety)과 데이터 캡슐화(Encapsulation)를 보장하기 위해, 모듈의 상태(State)와 행위(Behavior)를 담는 본체이다. - 메인 런루프 (Main Run-loop):
실제로 영원히 도는 무한 루프. 여기서 센서(uORB)를 읽고, 알고리즘 연산을 수행하며, 엑추에이터에 명령을 내린다.
이 3단계 구조 중, 가장 먼저 바깥세상(운영체제 커널)과 맞닿아 있는 피부껍질인 **‘모듈 엔트리 포인트(Entry Point)의 특수한 C 링키지 설계법’**을 다음 21.3.1 하위 단원에서 본격적으로 찢어발겨 보자.