21.1.1.1.2. NuttX RTOS 플랫(Flat) 빌드와 보호(Protected) 빌드 모드에서의 커스텀 모듈 동작 차이 분석
이전 단원에서 살펴본 ’플랫 메모리(Flat Memory)’의 잠재적 위험성, 즉 하나의 엉성한 커스텀 모듈이 메모리를 오염시켜 펌웨어 전체를 격추시킬 수 있다는 사실은 보안과 신뢰성이 생명인 항공 우주 산업에서 심각한 아킬레스건이다.
이를 극복하기 위해 최신 하드웨어(고성능 STM32H7 등 MPU(Memory Protection Unit)가 탑재된 프로세서)와 NuttX OS의 조합은, 기존의 무방비 상태였던 **플랫 빌드(Flat Build)**를 넘어서서 리눅스와 유사한 안정성을 흉내 내는 보호 빌드(Protected Build) 모드를 도입하기 시작했다. 여러분이 커스텀 앱을 짤 때 현재 기체가 어느 빌드 모드 위에서 도는지 아는 것은, 디버깅의 생사를 가르는 중요한 지표가 된다.
1. 플랫(Flat) 빌드 모드: 무법지대의 최대 성능 아키텍처
역사적으로 거의 모든 PX4-Pixhawk 시스템은 이 플랫 빌드 모드를 채택해 왔다. (예: px4_fmu-v2_default, px4_fmu-v5_default)
- 메모리 구조: 단 하나의 전역 물리 주소 공간(Single Address Space)을 가진다.
- 권한 분리(Privilege): 커널 스레드(OS 코어)와 유저 스레드(우리가 짜는 커스텀 애플리케이션) 사이에 어떠한 물리적 장벽이나 실행 권한(Ring)의 구분이 없다.
- 시스템 콜(System Call): 시스템 콜 함수(
open(),read(),ioctl())를 호출할 때 리눅스처럼 값비싼 컨텍스트 스위칭(Context Switching)이나 소프트웨어 인터럽트(SWI)가 발생하지 않는다. 그냥 평범한 C 함수 포인터를 점프(Jump)하는 것과 동일한 속도로 커널 메모리에 접근한다. - 커스텀 모듈의 영향: 우리가 짠 모듈은 극강의 성능과 제로-레이턴시(Zero-latency)를 누리지만, 포인터 연산 실수 한 번으로 커널 메모리(예: 스케줄러 구조체)를 덮어써 버릴 수 있다. 모듈이 죽으면(Crash), 전체 시스템이 함께 패닉(Kernel Panic)에 빠진다.
2. 보호(Protected) 빌드 모드: 메모리 보호 유닛(MPU)을 통한 격리
하드웨어 MMU(Virtual Memory)가 없는 마이크로컨트롤러 환경에서도, MPU(Memory Protection Unit)라는 하드웨어 레지스터를 활용하여 메모리에 구역(Region)을 나누고 접근 권한을 하드웨어적으로 통제하는 방식이 바로 보호 빌드(Protected Build)이다.
- 권한 분리(Privilege Separation): 이 모드에서는 펌웨어가 명확하게 **커널 영역(Kernel Space)**과 **유저 영역(User Space)**으로 쪼개진다. PX4의 코어 모듈(드라이버 등)은 커널 권한으로 돌지만, 여러분이 작성한 ’응용 계층의 커스텀 앱’은 제한된 유저 권한으로 강등된다.
- 물리적 격리벽: 커스텀 앱이 자신에게 할당되지 않은 커널 메모리 번지수나 다른 프로세스의 주소를 향해 포인터 쓰기(
*ptr = 1;)를 시도하는 순간, Cortex-M 코어의 MPU가 이를 즉시 낚아채어 메모리 관리 결함(Memory Management Fault) 인터럽트를 발생시킨다. - 커스텀 모듈의 생존주의: 이 방어막 덕분에 여러분의 조잡한(?) 커스텀 모듈 코드가 미쳐 날뛰더라도, 해당 유저 프로세스 하나만 깔끔하게 사살(Kill)될 뿐 PX4 커널과 모터 제어 루프는 살아남아 기체가墜落(추락)하는 참사를 막아준다.
2.1 보호 빌드 도입의 딜레마와 성능 패널티
보호 빌드는 궁극의 안전을 보장하지만, 드론 스택에서는 치명적인 독으로 작용할 수도 있다. 유저 공간에 갇힌 여러분의 모듈이 uORB나 하드웨어 드라이버와 통신하기 위해 인터페이스를 호출할 때마다, 이전에 공짜로 쓰던 함수 점프 대신 **무거운 시스템 콜 트랩(Trap)**을 거쳐야만 한다. 이는 필연적으로 CPU 오버헤드(Overhead)와 인터럽트 응답 지연(Latency)을 가중시킨다.
특히 1000Hz로 동작하는 고속 제어 펌웨어에서 매 틱마다 이 컨텍스트 격벽을 넘나드는 낭비는 치명적이다. 이 때문에 현재까지도 비행 제어에 수반되는 대부분의 PX4 인프라는 여전히 ‘가볍고 위험한’ 플랫 빌드를 기본값으로 유지하고 있으며, 보호 빌드는 개발자가 의도적으로 명시하지 않는 한 함부로 적용되지 않는다.
따라서 커스텀 모듈 개발자는 자신의 NSH C++ 애플리케이션 코드가 리눅스가 아닌 플랫 메모리 체제의 ’거대한 싱글 스레드 덩어리’의 일부로 탑재된다는 사실을 뼛속 깊이 새기고, 변수 메모리 접근 안전성(Thread-safety)을 스스로 증명해 내야만 한다. 이를 뒷받침하기 위해 다음 단원에서는 이 컨텍스트들의 분리가 응용 모듈과 드라이버 사이에서 어떻게 일어나는지, 런타임 생명주기를 상세히 파헤쳐 본다.