18.3.2.3 POSIX API(`open`, `close`, `read`, `write`, `ioctl`) 호출 시 uORB 내부 C++ 메서드로의 라우팅 기법

18.3.2.3 POSIX API(open, close, read, write, ioctl) 호출 시 uORB 내부 C++ 메서드로의 라우팅 기법

18.3.2.2절에서 다룬 file_operations 구조체 매핑의 원리를 바탕으로, 실제 비행 제어 모듈이 토픽을 퍼블리시(Publish)하거나 서브스크라이브(Subscribe)할 때 시스템 내부에서 벌어지는 연쇄적인 함수 호출(Call Stack) 라우팅 흐름을 단계별로 추적해 본다.

응용 계층(사용자 태스크)에서 순수 C 언어 기반의 POSIX 시스템 콜을 던졌을 때, 이 요청이 커널 계층을 뚫고 어떻게 uORB 매니저의 C++ 객체 내부로 정밀하게 꽂히는지 이해하는 것은 PX4 코어 아키텍처를 마스터하는 핵심이다.

1. open(): 구독의 시작과 객체 바인딩

모듈이 orb_subscribe(ORB_ID(sensor_gyro))를 호출하면 내부적으로는 open("/obj/sensor_gyro0", O_RDONLY) 통신이 발생한다.

  1. 이 POSIX open 콜은 즉시 NuttX 커널의 VFS 계층으로 진입한다.
  2. 커널은 /obj 트리를 탐색하여 sensor_gyro0 노드를 찾고, 여기에 할당된 file_operations 구조체를 참조하여 cdev_open 래퍼 함수를 실행한다.
  3. 핵심 매핑: 커널은 이 노드를 대표하는 inode 구조체 내부의 사용자 정의 포인터(i_private) 영역에 미리 숨겨두었던 cdev::CDev 객체의 메모리 주소를 꺼내, 새롭게 생성된 파일 디스크립터 구조체(file_t *filp)의 f_priv (Private Data) 공간에 은밀하게 이식한다.
  4. 이제 커널은 이 포인터를 캐스팅하여 ((cdev::CDev *)(filp->f_priv))->open(filp) 형태로 C++ 가상 메서드를 호출함으로써 라우팅을 마친다.

이후부터 이어지는 모든 입출력 호출은 파일 디스크립터(filp)만 제시하면 그 안에 숨겨진 C++ 객체 포인터를 통해 메서드로 즉시 점프할 수 있는 ’직통 고속도로’를 갖게 된다.

2. read()write(): 링 버퍼(Ring Buffer)와의 조우

매 스케줄링 틱(Tick)마다 센서 모듈이 데이터를 밀어 넣고 빼내는 과정이다.

  • orb_publish() 호출 시: 내부적으로 write(fd, data, size)가 실행된다. VFS 래퍼를 거쳐 C++ 런타임의 uORBDeviceNode::write() 메서드 내부로 점프한다. 이 메서드는 파라미터로 넘어온 data 버퍼를 자신(Node)이 관리하는 내부 링 버퍼 메모리에 고속 복사(memcpy)하고, 새로 데이터가 도착했음을 다른 구독자들에게 알리는 이벤트 브로드캐스트(Event Broadcast) 로직을 구동한다.
  • orb_copy() 호출 시: 내부적으로 read(fd, buffer, size)가 실행된다. 동일한 라우팅을 통해 uORBDeviceNode::read() 메서드로 진입하며, 클래스 내부의 링 버퍼 헤드(Head)와 테일(Tail) 인덱스를 계산하여 최신 데이터 단편을 사용자 버퍼(buffer)로 안전하게 복사해 준다.

3. ioctl(): 미들웨어 제어 명령의 터널링

단순한 데이터 복사(read/write) 외에, “스트림 업데이트 주기를 10ms로 강제해 줘” 라든가 “이 토픽의 큐(Queue) 사이즈가 얼마인지 알려줘” 와 같은 메타데이터 제어 명령이 필요할 때 ioctl (Input/Output Control) 함수가 동원된다.

모듈이 orb_set_interval(ob, 10)을 호출하면 이는 내부적으로 ioctl(fd, ORBIOCSETINTERVAL, 10) 치환되어 커널로 던져진다.
이 역시 uORBDeviceNode::ioctl() 메서드로 곧바로 라우팅되며, 메서드 내부의 거대한 switch-case 문을 통해 ORBIOCSETINTERVAL 상수를 캐치하고 내부 타이머 변수를 10ms 단위로 조정하는 기민한 객체 지향 처리가 이루어진다.


요약하자면, 사용자 모듈은 마치 단순 텍스트 파일(File)을 다루듯 하드코어한 C/C++ 객체(Object)에 접근하고 있다. 이 놀라운 인터페이스 착시 현상을 만들어내는 배후에는 포인터 은닉 기술(filp->f_priv)과 다형성을 십분 활용한 VFS V-Table 라우팅 기법이 숨 쉬고 있다.