19.4.2.1. 주기적인 태스크 슬립(usleep / px4_usleep) 설정
지금껏 우리가 PX4 코어 시스템에 이식해 온 모든 백그라운드 데이터 처리 데몬의 생명줄 컨트롤을 쥐고 있는 가장 핵심적인 흐름 뼈대는 바로 while (!thread_should_exit) 조건으로 묶여있는 메인 무한 루프 블록이다.
하지만 만약 이 미친 듯이 회전하는 무제한의 반복 톱니바퀴 루틴의 제일 밑단에, 코어 커널 수면(Sleeping) 제어 코드를 개발자의 오만으로 거세시켜버리면 시스템 보드 위에서 어떤 스케줄링 대참사가 발생할까?
바로 해당 데몬 스레드가 1 바퀴의 루프를 물리적으로 도는 데 걸리는 I/O 시간이 픽스호크 마더보드의 물리적인 CPU 오버클럭 최고 속도에 도달해 버려, 단 1초 만에 무한 루프를 수십만 번 공회전(Spin)하며 픽스호크 보드의 모든 가용 시스템 리소스(CPU 점유율 100%)를 모조리 탐욕스럽게 집어삼키는 CPU 기아(Starvation) 연산 독점 및 커널 일시 프리징(Freezing) 사태가 즉각 터지게 된다. 다른 모든 제어 연산 데몬들이 파괴되어 드론은 몇 초 안에 반드시 추락한다.
따라서 앞서 설계한 영리한 업데이트 확인(Update Check) 아키텍처이건, 가장 원시적인 단순 읽기(Simple Read) 아키텍처이건 간에 반드시 데몬 안의 모든 C++ 무한 루프의 끄트머리 하단에는 데몬 스레드를 스스로 강제 기절시켜 CPU 권한을 양보하는 쿨타임(Cooldown) 수면 제어 로직이 절대적으로 타설되어야만 한다.
1. px4_usleep(): OS 독립적인 마이크로초 단위의 무자비한 커널 수면제
가장 코딩 기초적이고 직관적인 스레드 단일 수면(Sleep) 통제 방법은, C++ 래퍼를 뚫고 px4_usleep() 코어 시스템 마스터 함수를 단 한 줄 호출하여, 이 프로세스 루프의 명시적 사이클 주파수(Hz)를 억지로 하드코딩 속도 제한(Throttle)을 걸어 제어하는 것이다. 리눅스 유닉스 표준의 usleep()을 PX4 시스템 환경에 맞게 크로스플랫폼 래핑(Wrapping)한 이 위대한 매크로는, 인자로 주어진 ‘마이크로초(Microseconds, us)’ 물리 시간 동안 현재 이방자 스레드의 상태를 실행(Running)에서 블로킹 대기(Blocking) 큐로 폭력적으로 던져버려, 다른 가엾은 코어 데몬들이 숨을 쉬고 CPU 캐시를 사용할 수 있게 통로를 시원하게 치워준다.
#include <px4_platform_common/tasks.h> // px4_usleep() 스케줄링 API를 뚫기 위한 코어 태스크 헤더 훅업
while (!thread_should_exit) {
// 1. 아주 가벼운 버퍼 껍데기 업데이트 확인 노크 로직
// orb_check(...)
// 2. 버퍼 갱신 시 메모리 카피 및 극악의 비즈니스 연산 수행
// if (updated) orb_copy(...)
// 3. [시스템의 대승적 생명선] 내 데몬이 스스로 통제권을 꺾고 CPU 점유율을 OS에 반환한 채 깊고 긴 수면에 빠짐
// 200,000us = 200ms = 0.2초의 물리적 대기 (즉, 이 엄청난 데몬의 최대 동작 주기는 극한의 5Hz로 고정되어 박제됨)
px4_usleep(200000);
}
2. px4_usleep의 1차원적 한계와 비동기 반응성(Reactivity)의 완전한 부재
이 타이머 기반의 방식은 개발자의 코드가 1차원적으로 극구 단순해진다는 유혹적인 장점이 있지만, 인터럽트 아키텍처 관점에서 고해부 해보면 대단히 치명적인 비동기적 딜레마를 안고 있다.
내 무지성 데몬이 하단 px4_usleep 타이머를 맞고 0.2초 동안 강제로 기절해 자빠져 있는 도중에, 만약 링 버퍼망 상단에 너무나 다급하고 중요한 센서 트랜잭션 데이터들이 100개가 한꺼번에 폭풍처럼 쏟아져 들어왔다면 어쩔 텐가? 내 스레드는 운영체제 알람이 스스로 울리기 전까지 바보처럼 눈을 감고 있으므로, 이 0.2초 사이에 쏟아진 귀중한 통신 프레임 덩어리들을 실시간으로 번개처럼 낚아챌 런타임 타임라인을 모조리 멍청하게 놓치게 되고 만다. 데이터는 버퍼 속에서 그냥 덮어씌워져 유실된다.
즉, px4_usleep 타이머 하나에 구차하게 전적으로 의존하는 1세대 폴링(Polling) 방식은 철저하고 일방적으로 **“나는 네가 데이터를 쏘든 말든 1도 관심 없고, 그저 내가 하드코딩 정해놓은 거만하고 느린 타이머 주기에 맞춰 내가 원할 때만 시스템 버퍼 껍데기를 이기적으로 열어 검사하겠다”**라는 아주 오만한 수동적(Passive) 임시방편 아키텍처 루틴에 불과한 것이다.
진정한 고주파 리얼타임 하드웨어 인터럽트 반응성(Reactivity) 0.1ms의 한계 지연(Latency) 통제권을 얻기 위해서는, 이후 고도화 단원들에서 본격 등장할 poll() 시스템 콜이나 uORB::SubscriptionInterval 래퍼 클래스 같은 고급 비동기 이벤트 기상(Wake-up Signal) 스케줄링 함수가 반드시 당신의 시스템 코드에 수혈 타설되어야 한다.