18.5.1.2. `uORBDeviceNode::open()` 호출 시 내부 `Subscriber` 구조체 리스트 추가 및 VFS 참조 카운터(Reference Count) 관리

18.5.1.2. uORBDeviceNode::open() 호출 시 내부 Subscriber 구조체 리스트 추가 및 VFS 참조 카운터(Reference Count) 관리

PX4-Autopilot의 uORB 메커니즘에서 VFS(Virtual File System) 계층의 open() 시스템 콜이 실행된 후, 실질적인 제어권은 uORB 코어 객체인 uORBDeviceNode::open(struct file *filp) 로 전달된다. 본 절에서는 해당 함수 내부에서 다수의 구독 요청을 어떻게 구조적으로 수용하고 메모리를 안전하게 통제하는지에 대한 C++ 로직을 심층 해부한다. (분석 기준: PX4 펌웨어 최신 브랜치)

1. 구독자 트래킹 (Subscriber Tracking) 메커니즘

uORBDeviceNode 클래스는 단일 uORB 토픽 인스턴스에 대한 모든 상태와 공유 링 버퍼(Ring Buffer)를 캡슐화하고 있다. 여러 모듈(예: ekf2 상태 추정기, mc_pos_control 위치 제어기)이 동일한 토픽에 동시다발적으로 구독을 요청할 경우, 시스템은 각 구독자의 데이터를 읽어간(Fetched) 진행 상태를 독립적으로 관리해야 한다.

이를 위해 open() 메서드가 호출되면 노드 객체 내부에서 다음 작업이 수행된다.

  1. 동적 할당을 통한 Subscriber 인스턴스화: 시스템의 힙 또는 메모리 풀 내에서 Subscriber라는 내부 상태 관리용 구조체가 새롭게 할당(Allocation)된다.
  2. 구독자 리스트 병합 (Linked-list/Array Insertion): 할당된 Subscriber 객체는 uORBDeviceNode가 내부적으로 유지하는 구독자 목록(주로 연결 리스트 방식)에 삽입된다.
  3. 파일 핸들과의 포인터 결합(Pointer Binding): 커널 레벨에서 넘어온 struct file *filp의 내장 프라이빗 데이터 필드(filp->f_priv)에 방금 생성한 Subscriber 객체의 메모리 주소를 위치시킨다. 이 디자인적 이점 덕분에 후속 read()ioctl() 호출 시 O(1)의 복잡도로 현재 호출자가 어떤 구독자인지 정확히 역추적할 수 있다.
classDiagram
    class uORBDeviceNode {
        -uint8_t* _buffer
        -uint16_t _subscriber_count
        -List~Subscriber~ _subscribers
        +open(filp)
        +read(filp, buffer)
        +write(buffer)
    }
    class Subscriber {
        -unsigned generation
        -void* priv_data
    }
    class struct_file {
        +void* f_priv
        +int f_oflags
    }
    uORBDeviceNode "1" *-- "n" Subscriber : 관리 (Manages)
    struct_file --> Subscriber : 포인터 참조 (Points to)

2. VFS 참조 카운터(Reference Count)의 역할 및 생명 주기 관리

임베디드 비행 제어기 환경에서 메모리 관리는 시스템 안정성의 핵심 지표이다. 여러 태스크가 토픽 생태계를 자유롭게 구독하거나 필요 시 구독 해제(close())할 수 있으므로, 단 하나의 태스크도 더 이상 토픽을 주시하지 않고 발행자도 없을 때만 해당 버퍼용 동적 메모리를 해제해야 댕글링 포인터(Dangling Pointer)나 메모리 누수로 인한 시스템 장열(Crash)을 극복할 수 있다.

uORBDeviceNode::open() 내부에서는 참조 카운트 메커니즘, 즉 _subscriber_count 멤버 변수 및 OS 레벨의 VFS inode 참조 카운터를 원자적으로(Atomically) 가산시킨다.

2.1 생명 주기(Lifecycle) 시나리오

  1. 최초 구독 시 (Ref Count 0 -> 1): 토픽 버퍼가 생성되지 않았다면 초기화 루틴을 거쳐 링 버퍼 등 메모리를 확보한다. uORB 노드가 비로소 활성화(Active) 단계에 진입한다.
  2. 추가 구독 시 (Ref Count N -> N+1): 링 버퍼를 새로 생성하는 중복 투자 없이 앞서 설명한 Subscriber 구조체 인스턴스 매핑만 추가하여 동적 메모리 확장을 억제한다.
  3. 구독 해제 시 (close(), Ref Count N -> N-1): 태스크가 완전히 소멸하거나 명시적으로 구독 디스크립터를 반납할 경우 참조 카운트가 감산된다.
  4. 카운터 소멸 (Ref Count 1 -> 0): 모든 구독자가 구독을 정지하였으며, 토픽 발행자(Publisher)마저도 노드를 소유하지 않았다면 비로소 해당 uORBDeviceNode 객체의 소멸자(Destructor)가 작동하여 힙(Heap) 공간으로 객체가 메모리 파편화 없이 안전하게 반환된다.

이러한 참조 카운터 매커니즘은 다중 코어 또는 다중 스레드 환경에서도 락프리 및 안전한 메모리 해제 철학을 지향하는 PX4-Autopilot의 아키텍처 내에서 무결성을 방어하는 절대적인 구조적 기반이 된다.