19.1.3.2. 모듈의 진입점(Main function) 및 데몬(Daemon) 실행 구조 설계

19.1.3.2. 모듈의 진입점(Main function) 및 데몬(Daemon) 실행 구조 설계

앞선 장에서 단순히 텅 빈 파일 사이즈 0포맷으로 타설해 둔 px4_uorb_example.cpp 메인 소스 파일에 비로소 잔혹한 생명력을 불어넣을 차례이다. PX4-Autopilot 생태계 내에서 동작하는 모든 실행 가능한 모듈(Executable Module) 스레드는, 단순히 일반 운영체제(OS)가 1회성으로 호출하고 스택 메모리를 매몰차게 날려버리는 int main() 구조를 맹목적으로 따르지 않는다.
초당 수백, 수천 번의 인터럽트를 버텨내야 하는 하드 리얼타임 OS(NuttX 커널) 환경에서는, 컴파일된 바이너리가 콘솔 셸(NuttShell, NSH) 명령어 타격의 형태로 즉각 파싱되고 호출될 수 있도록, __EXPORT 매크로 특권 권한이 고도로 부여된 아주 특별한 C 링키지(C Linkage) 시그니처 뼈대를 진입점(Entry Point)으로 엄격하게 요구한다.

1. 커널 셸 콘택트 포인트: __EXPORT 메인 함수 시그니처 규칙의 이해

가장 바깥쪽 외부 껍질이자 OS 커널과 단독으로 맞닿아 교신하는 모듈의 최초 진입점은 반드시 자신의 파일명과 동일한 접두사를 가진 _main 함수로 C 언어 문법 스타일로 명시되어야만 한다. 이를 소스 코드의 최상단에 다음과 같이 가장 견고하게 타설한다.

#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/log.h>

// NuttX 하위 커널 셸(NSH) 스케줄러에서 이 모듈의 심볼을 찾아 즉각 실행할 수 있도록 
// 무자비한 C++ 망글링을 억제하는 C 링키지 보장 및 강제 심볼 외부 추출
extern "C" __EXPORT int px4_uorb_example_main(int argc, char *argv[]);

int px4_uorb_example_main(int argc, char *argv[])
{
    // C++ 코어 비즈니스 로직 세계로의 진입
    PX4_INFO("uORB Example Module Executed and Ready!");
    return 0;
}

이 압축된 단 몇 줄의 헤더 코드가 시스템 생태계에 내포하는 아키텍처적 무게감은 매우 거대하다. extern "C"는 C++ 컴파일러 특유의 난해한 네임 망글링(Name Mangling, 오버로딩 지원을 위해 함수명을 암호화하는 행위) 현상을 무력으로 강제 억제하여 링커(Linker)가 함수의 이름을 문자열 텍스트 날것 그대로 찾을 수 있도록 뚫어준다. __EXPORT 매크로는 해당 메모리 심볼이 펌웨어 마스터 바이너리 테이블 밖으로 당당하게 공개되어, 개발자가 터미널 셸에서 px4_uorb_example start 커맨드를 직접 타이핑했을 때 픽스호크 셸 스케줄러가 곧바로 이 C++ 함수 주소로 콘텍스트 스위칭(Context Switching)할 수 있도록 물리적인 차원문 루트를 개방해 주는 것이다.

2. 데몬(Daemon) 라이프사이클 제어 통제권: Start, Stop, Status 아키텍처

진정한 수준의 PX4 코어 통신 모듈은 한 번 불리고 시시하게 퇴장하는 일방통행형 쉘 스크립트 나부랭이가 아니다. 펌웨어 부팅 시 백그라운드 스레드로 시스템에 융합되어 주기적인 인터럽트 폴링 미션을 기약 없이 평생 수행해야 하는 **절대적인 파수꾼 데몬(Daemon)**의 형태를 띠어야만 한다.
따라서 main 함수 내부는 반드시, 사용자(혹은 시작 부트 스크립트 RCS)가 입력한 CLI 인자 배열(argv)을 정밀하게 파싱하여, 자신의 복제 분신 스레드를 백그라운드 시스템망에 깊숙이 띄울지(start), 현재 죽지 않고 정상적으로 작동하여 살아있는지 점검할지(status), 혹은 동작 중인 스레드를 무자비하게 파괴하고 메모리 자원을 우아하게 반환할지(stop) 결정하는 3단 분기문 뼈대를 완벽하게 지녀야 한다.

#include <string.h> // 문자열 strcmp 파싱용 C 표준 헤더

// 백그라운드 워커 스레드가 살아서 실행 중인지 여부를 감시하는 전역(Global) 상태 플래그
static bool thread_running = false;
// 메인 루프를 강제로 탈출시켜 스레드를 파괴하기 위한 킬 스위치(Kill Switch)
static bool thread_should_exit = false;

// 모듈의 사용법(Help) 매뉴얼을 터미널 콘솔에 렌더링하는 헬퍼 함수
static void usage() {
    PX4_INFO("Usage: px4_uorb_example {start|stop|status}");
}

int px4_uorb_example_main(int argc, char *argv[])
{
    if (argc < 2) {
        usage();
        return 1;
    }

    // [1] 데몬 기상명령 파싱 플로우
    if (!strcmp(argv[1], "start")) {
        if (thread_running) {
            PX4_WARN("Fatal: Module is already running in background!");
            return 0; // 데몬 충돌 에러 방어
        }
        thread_should_exit = false;
        
        // TODO: 향후 이 지점에서 백그라운드 무한 루프 워커 스레드 직접 생성(px4_task_spawn_cmd)
        // 또는 WorkQueue 시스템 스케줄러 등록 로직을 강력하게 발동시킴
        thread_running = true;
        
        PX4_INFO("Module daemon started successfully.");
        return 0;
    }

    // [2] 데몬 자결명령 파싱 플로우
    if (!strcmp(argv[1], "stop")) {
        if (!thread_running) {
            PX4_WARN("Fatal: Module is not currently running!");
            return 0; // 없는 데몬 파괴 요청 검열
        }
        thread_should_exit = true; // 통신 백그라운드 C++ 루프 파괴 지시
        
        // TODO: 스레드가 스핀 루프를 탈출하여 완전히 종료될 때까지 대기(Wait) 및 C++ 메모리 자원 회수
        thread_running = false;
        
        PX4_INFO("Module daemon stopped carefully and successfully.");
        return 0;
    }

    // [3] 데몬 헬스체크(Health Check) 파싱 플로우
    if (!strcmp(argv[1], "status")) {
        if (thread_running) {
            PX4_INFO("Module status: currently running & alive.");
        } else {
            PX4_INFO("Module status: NOT running.");
        }
        return 0;
    }

    // 예외 처리: 알 수 없는 쓰레기 커맨드 타격 시
    usage();
    return 1;
}

초보 코딩 개발자들은 단순히 데이터를 던지고 받는 것에 급급하여 이 길고 복잡한 C++ 뼈대 보일러플레이트(Boilerplate) 코드를 꼼꼼히 작성하는 행위 자체를 번거롭고 불필요하게 여긴다.
그러나 기억하라. 이러한 완벽하고 흠결 없는 데몬의 라이프사이클(Lifecycle) 제어 권한 뼈대를 튼튼하게 타설해야만, 비로소 우리가 향후 코딩할 uORB 커스텀 메시지를 하드웨어가 100Hz로 미친 듯이 전역에 발행(Publish)하는 극한의 모터 반복 루프 while (!thread_should_exit) 컨트롤러 로직을, 픽스호크 백그라운드 메모리에 무사히 띄울 수 있는 물리적 자격과 신용이 커널 셸로부터 비로소 주어지는 것이다.
또한 비행 중 시스템이 상상치 못한 위급 상황(Failsafe)에 빠져 이 모듈을 수동으로 긴급 셧다운 시킬 때조차, 스택에 메모리 고아(Memory Orphan)나 버퍼 찌꺼기를 일절 남기지 않고 가장 우아하고 아름답게 스스로 죽음을 맞이할 수 있는 소프트웨어적 존엄성을 확보하게 되는 근본이기도 하다.