21.3.3.1. `make px4_sitl jmavsim` 환경에서의 모듈 실행 컨텍스트

21.3.3.1. make px4_sitl jmavsim 환경에서의 모듈 실행 컨텍스트

리눅스 터미널 창에 make px4_sitl jmavsim 이라는 명령어를 밀어 넣고 엔터를 치면, 화면에는 수백 줄의 녹색 컴파일 로그가 쏟아지더니 이내 웅장한 3D 그래픽의 쿼드코터(JMAVSim) 창이 하나 뜬다. 그리고 터미널의 가장 마지막 줄에는 pxh> 라는, 평소 픽스호크 USB 포트에서나 보던 익숙하고도 낯선 프롬프트가 여러분을 기다리고 있을 것이다.

이 가상화된 pxh> 셸 환경 아래서 여러분이 만든 custom_app start 명령어를 쳤을 때, 여러분의 코드는 도대체 누구의 메모리 위배에서, 어떤 통제를 받으며 실행되고 있는 것일까?

이것을 이해하려면 SITL(Software In The Loop) 빌드가 만들어낸 유일무이한 바이너리 실행 파일(Executable)의 정체를 깨달아야만 한다.

1. 단일 공룡 프로세스(Monolithic Process)의 탄생

픽스호크 보드(NuttX) 용으로 빌드했을 때는 px4_fmu-vX_default.px4 라는 형태의 펌웨어 덩어리가 만들어졌다. 하지만 노트북용으로 빌드된 SITL 아웃오브트리 폴더(build/px4_sitl_default/)를 뒤져보면, bin/px4 라는 아주 평범한 리눅스 실행 파일(ELF Binary)이 하나 덩그러니 놓여 있는 것을 볼 수 있다.

make px4_sitl jmavsim 명령어는 내부적으로 가상 시뮬레이터(JMAVSim)를 자바(Java) 프로세스로 먼저 띄운 다음, 뒤이어 바로 저 bin/px4 리눅스 실행 파일을 백그라운드 프로세스로 실행시킨 것이다.

bin/px4 프로세스 하나가 가지는 의미는 절대적이다.

  1. 수백 개의 PX4 모듈(센서, 제어기, 네비게이터, 그리고 여러분의 custom_app)이 리눅스의 독립된 프로세스(Process)들로 각자 쪼개져서 실행되는 것이 절대 아니다.
  2. PX4의 모든 100만 줄짜리 펌웨어 코드와 uORB 메시지 브로커가 모조리 링크되어 하나의 거대한 **단일 공룡 프로세스(px4)**로 묶여져서 우분투 OS 위로 올라간다.
  3. 우리의 C++ custom_app_main 함수 역시 이 거대한 공룡의 몸속 어딘가에 C 함수 포인터 형태로 조용히 박혀 있다.

2. 우분투 프로세스 안의 작은 우주: pxh>

터미널 하단에 떠 있는 pxh> 프롬프트는 우분투의 bash 셸이 아니다. 이 프롬프트는 px4 실행 프로세스가 자기 코드 내부에서 while(1) 루프를 돌리며 **가짜로 흉내 내고 있는 가상 NSH(NuttShell)**이다.

여러분이 pxh> custom_app start 라고 키보드를 치면,

  1. 우분투 OS는 이 텍스트를 px4 프로세스의 표준 입력(stdin)으로 밀어 넣어 준다.
  2. px4 프로세스 내부의 문자열 파서가 이를 받아들여, 자기 몸통 안에 링크되어 있는 심볼 테이블을 뒤져 custom_app_main 함수의 메모리 주소를 찾아낸다.

여기까지는 실제 기체(NuttX)와 완벽하게 똑같다. 하지만 함수를 찾아낸 직후 이 함수를 어떻게 독립적인 실행 흐름(Thread)으로 만들어서 띄워줄 것인가에서 결정적인 차이가 발생한다. 실제 기체였다면 NuttX 커널 API를 호출했겠지만, 지금 우리는 우분투 리눅스 위에 떠 있다.

이 거대한 단일 리눅스 px4 프로세스 내부에서, 여러분의 모듈을 독립적인 스레드로 갈라치기(Spawn)하는 리눅스 POSIX API(pthreads) 매핑의 오묘한 세계를 다음 장(21.3.3.1.1)에서 속 시원하게 파헤쳐보겠다.