9.4.2.1 C 언어 계층 구조체 압축(Packed Struct) 강제를 통한 패딩(Padding) 블록 차단 구조
분산 로보틱스 생태계의 끝단(Edge)에 위치한 C/C++ 펌웨어 엔지니어들은 종종 직렬화 프레임워크(Protobuf 등)조차 탑재할 수 없을 정도로 절박한 8비트, 혹은 32비트 마이크로컨트롤러(MCU)와 조우하게 된다. 이 극도로 피폐한 환경에서는 순수한 C 구조체(C Struct)의 메모리 버퍼를 그대로 잘라 Zenoh의 z_put 명령에 직송하는 이른바 날것(Raw)의 직렬화 프로토콜에 의존할 수밖에 없다.
그러나 구조체를 바이트 스트림으로 취급하려는 태도는, 현대 C 컴파일러가 자행하는 메모리 패딩(Memory Padding)의 역린을 건드리는 파멸적 행위다. 본 절에서는 컴파일러의 자의적인 패딩 블록 삽입 현상을 억제하고, 순수 C 구조체의 바이너리 규격을 정확히 통신 프로토콜 레이아웃과 일치시키는 구조체 압축(Packed Struct) 강제 기법을 전개한다.
1. 컴파일러의 독단적 패딩(Padding) 최적화 현상
CPU가 메모리를 스캔할 때, 32비트 아키텍처는 보통 4바이트(Word) 단위로, 64비트 아키텍처는 8바이트 단위로 데이터를 긁어들인다. 이에 부응하기 위해 gcc나 clang 같은 커스텀 펌웨어 컴파일러는 프로그래머가 명시하지 않은 빈 공간(Hole)을 구조체 사이에 강제로 욱여넣는다.
// 보편적인 C 구조체 레이아웃
struct RobotStatus {
uint8_t battery_status; // 1바이트 소모
// 컴파일러는 이곳에 어떠한 합의도 없이 3바이트의 패딩(쓰레기값)을 삽입한다!
uint32_t encoder_ticks; // 4바이트 소모 (4의 배수 주소에 안착)
uint16_t error_code; // 2바이트 소모
// 여기에도 2바이트의 패딩이 강제 삽입된다.
};
논리적으로 위 구조체의 크기는 1 + 4 + 2 = 7 bytes 가 되어야 마땅하나, sizeof(struct RobotStatus)를 호출하면 무려 12 bytes가 튀어나온다. 만약 이 구조체의 시작 포인터를 네트워크망으로 송출해버리면, 수신 측 파이썬 서버에서는 존재하지도 않는 패딩 쓰레기 바이트가 데이터 값으로 편입되어 배터리 수치가 240만이라는 미친 값으로 오역되는 파국에 도달한다.
2. 팩 플래그(Attribute Packed)의 도입과 파이프라인 무결성
이기종 네트워크 사이에 빈틈없는 바이트 배열을 전이(Transfer)하기 위해, C 프로그래머는 컴파일러의 최적화 본능을 지배하고 목줄을 채우는 전용 전처리(Preprocessor) 명령어를 포진시켜야 한다. 컴파일러 종류에 따라 양식이 다르지만, 가장 대중적으로 사용되는 강력한 무기가 __attribute__((packed)) 구문이다.
// [강건한 통신 직렬화를 위한 구조체 압축 런북]
typedef struct __attribute__((packed)) {
uint8_t battery_status;
uint32_t encoder_ticks;
uint16_t error_code;
} CompressedRobotStatus_t;
이 압축 지시어(Packed Directive)를 발포하는 순간, 컴파일러는 성능 향상을 위해 뚫어놓았던 패딩 여백을 완전히 착즙 수축(Compression)시켜버린다. sizeof(CompressedRobotStatus_t)의 크기는 수학적 진실인 정확히 7 bytes로 강하하며, 이 7바이트의 연속된 바이너리가 그대로 Zenoh 소켓 인터페이스에 올라탐으로써 클라우드 영역과 1:1 바이트 정합성(Byte Accuracy) 조약이 성립된다.
3. 언라인드 억세스(Unaligned Access) 오류 대응 및 버스 폴트(Bus Fault) 방어
그러나 압축(Packed) 강제는 결코 공짜가 아니다. 패딩 껍질을 벗겨버림으로써 CPU는 끔찍한 대가를 치르게 된다.
ARM Cortex-M0와 같은 구형 칩셋이나 치밀한 하드웨어 아키텍처에서는, 메모리 정렬 규칙(4의 배수 번지) 위배 상태인 압축 구조체의 내부 변수에 직접 접근(status.encoder_ticks = 100) 하려고 시도할 때, 커널에서 즉시 보호 메커니즘을 작동시켜 하드웨어 예외인 버스 오류(Bus Fault) 판정과 함께 단말기를 코어 덤프(Core Dump) 시킨다.
따라서 팩(Packed) 구조체를 관장하는 시스템 프로그래머는 어떠한 경우에도 압축 구조체 내부의 필드를 연산 변수로 사용하지 말아야 하는 절대 원칙을 고수해야 한다.
통신이 닿자마자 압축 구조체의 메모리 파편을 네트워크 소켓에서 떠낸 직후, 연산 스택에 존재하는 ‘패딩이 있는 일반 런타임 구조체’ 속으로 각각의 요소들을 값 복사(Value Copy, 또는 memcpy)로 대피시킨 후에만 비로소 디코딩 데이터로 활용해야 한다. 구조체 압축은 오직 통신 전송(Wire Transmission) 구간을 안전하게 돌파하기 위한 임시 이동 우주복에 불과함을 뼈저리게 인식하라.