21.7.2.1.1. 센서 드라이버가 토픽을 발행(Publish)하는 즉시 콜백이 호출되어 `ScheduleNow()`로 해당 모듈을 스케줄러 큐 최상단에 푸시(Push)하는 지연 시간 제로화(Zero-latency) 기법

21.7.2.1.1. 센서 드라이버가 토픽을 발행(Publish)하는 즉시 콜백이 호출되어 ScheduleNow()로 해당 모듈을 스케줄러 큐 최상단에 푸시(Push)하는 지연 시간 제로화(Zero-latency) 기법

앞 장에서 우리는 _sensor_gyro_sub.registerCallback()을 통해 내 모듈(mc_rate_control)을 센서 토픽의 호적 공간에 묶어놓는 과정(Binding)을 살펴보았다.
이제 시점을 반대로 돌려, 우체통(uORB 토픽)에 데이터를 집어넣는 ‘우체부’ 입장, 즉 **센서 드라이버(Sensor Driver)**의 관점에서 커널 내부의 극적인 지연 시간 제로화(Zero-latency) 폭발 과정을 슬로우 모션으로 뜯어보자.

1. ISR(Interrupt Service Routine)의 찰나

  1. 하드웨어 인터럽트 발생: 드론 비행 중, 픽스호크 보드에 탑재된 물리적인 IMU 칩셋(예: BMI088)이 각속도 측정을 마치고 픽스호크 메인 MCU(STM32)의 Data Ready(DRDY) 핀으로 3.3V 전기 신호를 쏜다.
  2. 센서 스레드 기상: 픽스호크의 인터럽트 핸들러가 이 신호를 낚아챈다. 인터럽트 핸들러는 센서 드라이버(bmi088 모듈)를 즉각 깨운다.
  3. 데이터 복사 및 발행: 센서 드라이버는 I2C/SPI 버스를 통해 따끈따끈한 각속도 데이터를 읽어와서 sensor_gyro_s 구조체에 채운 뒤, 대망의 한 줄을 커널에 날린다.
    orb_publish(ORB_ID(sensor_gyro), _gyro_pub_fd, &gyro_data);
    

자, 이 `orb_publish()` 함수 스코프 안으로 들어가는 바로 그 짧은 나노초 단위의 순간에 uORB 코어 스레드의 '푸시(Push)' 매직이 펼쳐진다.

## 2. `orb_publish()` 내부의 콜백 연쇄 폭발


`orb_publish` 코드가 실행되면, uORB 매니저는 먼저 메모리 락을 걸지 않고(Lock-free) 게시판에 `gyro_data`를 조심스럽게 복사(Update)한다.
그다음, 이 게시판(`sensor_gyro` 토픽)에 혹시 알람줄(Callback)을 매달아 놓은 구독자가 있는지 리스트를 쫙 스캔한다.

이 리스트에서 앞 장에서 우리가 예쁘게 묶어두었던 `mc_rate_control` 모듈의 `_sensor_gyro_sub` (SubscriptionCallbackWorkItem) 객체가 발각된다.

```cpp
// uORB 내부 스코프 (단순화된 개념 코드)
void uORB_Publisher::publish(const void *data) {
    // 1. 게시판(Shared Memory) 데이터 최신화
    update_internal_data(data);

    // 2. 콜백 리스트 순회 및 발동!
    for (auto cb_node : _callbacks) {
        cb_node->call(); // 구독자 모듈을 강제로 때림!
    }
}

cb_node->call()이 실행되는 순간, 제어권은 순식간에 mc_rate_control 모듈로 넘어오게 되고, SubscriptionCallbackWorkItem 클래스 내부의 구현체인 ScheduleNow()가 직격으로 호출된다.

3. ScheduleNow(): 버스 추월차선 탑승

ScheduleNow()가 불려진다는 것은 앞선 타이머 예약(ScheduleOnInterval) 체제와는 그 품격이 다르다.

이 함수는 “다음 타이머 주기가 언제지? 아 3ms 남았네, 3ms 뒤에 깨워야지.” 따위의 계산을 일절 하지 않는다.
“지금 당장 무조건! 내 Work Item(mc_rate_control)을 wq_rate_ctrl 스레드 풀 버스의 ’가장 앞자리(Head of the Queue)’에 강제로 집어넣어라!”

  1. ISR(인터럽트) 발생부터 센서 드라이버가 데이터를 읽어 orb_publish를 호출하기까지 약 100us 소요.
  2. orb_publish 내부에서 콜백이 트리거되어 mc_rate_controlScheduleNow()가 호출되어 스케줄러 큐 최상단에 꽂히기까지 약 5us 소요.
  3. 스레드 컨텍스트 스위칭(Context Switching)이 발생하여 mc_rate_control::Run() 함수 첫 줄이 실행되기까지 약 10~20us 소요.

결과적으로 물리적 센서의 미세한 움직임이 발생한 지 불과 0.15ms(150us) 이내에 드론의 핵심 C++ 제어 로직(Run())이 완벽한 구조체 데이터를 손에 쥔 채 실행되는 경이로운 지연 시간 제로화(Zero-latency) 연쇄 반응이 완성된 것이다!

이토록 미친듯한 속도로 코어가 돌아가면서도 시스템이 멈추지 않는 것은 uORB가 데이터를 출판하고(Publish) 읽어올(Subscribe) 때 무거운 스핀락(Spin-lock)을 걸지 않기 때문이다. 도대체 데이터가 덮어씌워지는 와중에 어떻게 락 없이(Lock-free) 데이터 파편화 현상을 막아내는지, 그 uORB 코어 스레드의 진정한 비밀을 다음 챕터 21.7.3장(Lock-Free 발행)에서 벗겨보자.