18.6.2 데이터 발행 시 스레드 기상(Wake-up) 트리거 로직
px4_poll()을 통해 다수의 제어기 스레드들이 각자의 세마포어(Semaphore)를 쥐고 uORBDeviceNode 주변에서 깊은 수면(Sleep)에 빠져 있다면(18.6.1절), 누군가는 이들의 알람시계 역할을 해주어야 한다.
이 알람을 울리는 주체는 다름 아닌 퍼블리셔(Publisher) 이며, 기상 트리거의 발동 시점은 데이터가 링 버퍼에 완전히 쓰인 직후 단 1 마이크로초의 지연도 없이 즉각적으로 이루어진다.
1. uORBDeviceNode::write()의 대미를 장식하는 poll_notify()
18.4.2절에서 우리는 퍼블리셔가 orb_publish()를 호출했을 때, 락프리(Lock-free) 환경에서 memcpy를 수행하고 세대 카운터(Generation Counter)를 증가시키는 과정을 살펴보았다. 사실 write 함수의 진정한 마지막 임무는 데이터 복사가 아니라 ‘이벤트 브로드캐스팅(Event Broadcasting)’ 에 있다.
모든 메모리 복사가 안전하게 끝나면, 퍼블리셔 시스템 콜은 종료되기 직전에 커널 내부의 poll_notify(POLLIN) 함수를 강하게 타격(Trigger)한다.
2. 브로드캐스트 기상(Wake-up)의 4단계 연쇄 반응
poll_notify() 가 불리는 순간, 커널 내부에서는 잠든 스레드들을 깨우기 위한 연쇄 반응이 번개처럼 일어난다.
- 대기열(Wait Queue) 순회: 커널은 이 데이터가 쓰인
uORBDeviceNode객체 내부에 비밀스럽게 유지되고 있는 ’폴링 대기열 리스트(Poll Waiters List)’를 꺼내어 순회하기 시작한다. 여기에는 이 토픽의 업데이트를 애타게 기다리다 잠든(Blocked) 모든 스레드의 정보가 담겨 있다. - 이벤트 매칭: 대기 중인 스레드가 요구했던 이벤트 타입(
POLLIN, 데이터 수신 대기)과 현재 발생한 이벤트가 일치하는지 확인한다. - 세마포어 포스트(Semaphore Post): 스레드가 잠들 때 사용했던 RTOS 동기화 객체(NuttX의 경우
nxsem_post등)에 시그널을 날린다. - 스케줄러의 개입 (Ready Queue 전이): 이 시그널을 맞은 구독자 스레드들은 즉각적으로 OS의 Blocked 상태에서 해제되어 Ready-to-Run (실행 가능) 큐로 멱살이 잡혀 끌려 올라온다.
만약 깨어난 구독자 스레드의 우선순위(Priority)가 현재 실행 중인 퍼블리셔 스레드보다 높다면, 퍼블리셔의 남은 작업마저 선점(Preempt)해 버리고 그 즉시 CPU 제어권을 탈취하여 orb_copy를 실행해 버린다.
이처럼 poll_notify 메커니즘은 단순한 알림을 넘어, RTOS 스케줄러의 심장부에 직접 개입하여 폴링의 지연 시간(Latency)을 ‘수 나노초~마이크로초’ 단위의 하드 리얼타임(Hard Real-time) 수준으로 압축해 내는 PX4 비동기 성능의 핵심 엔진이다.