programing

포장된 구조물은 휴대 가능합니까?

copysource 2022. 8. 9. 23:29
반응형

포장된 구조물은 휴대 가능합니까?

Cortex-M4 마이크로컨트롤러에 코드가 있는데 바이너리 프로토콜을 사용하여 PC와 통신하고 싶습니다.현재, GCC 고유의 구조물을 사용하고 있습니다.packed기여하다.

다음은 대략적인 개요입니다.

struct Sensor1Telemetry {
    int16_t temperature;
    uint32_t timestamp;
    uint16_t voltageMv;
    // etc...
} __attribute__((__packed__));

struct TelemetryPacket {
    Sensor1Telemetry tele1;
    Sensor2Telemetry tele2;
    // etc...
} __attribute__((__packed__));

질문입니다.

  • 같은 정의를 사용한다고 가정하면TelemetryPacketMCU와 클라이언트 앱의 구조는 위의 코드를 여러 플랫폼에서 이식할 수 있습니까? (x86과 x86_64에 관심이 있으며 Windows, Linux 및 OS X에서 실행할 필요가 있습니다.)
  • 다른 컴파일러는 동일한 메모리 레이아웃으로 패킹된 구조를 지원합니까?어떤 구문을 사용합니까?

편집:

  • 네, 포장된 구조물이 비표준인 건 알지만, 사용을 고려할 만큼 유용해 보입니다.
  • 저는 C와 C++에 모두 관심이 있지만 GCC가 다르게 취급할 것이라고는 생각하지 않습니다.
  • 이 구조들은 상속되지 않고 아무것도 상속되지 않습니다.
  • 이러한 구조에는 고정 크기의 정수 필드 및 유사한 패킹된 구조만 포함됩니다.(전에도 수레에 화상을 입은 적이 있습니다...)

전술한 플랫폼을 고려하면, 네, 패킹된 구조물은 완전히 사용할 수 있습니다.x86 및 x86_64는 항상 비정렬 액세스를 지원하며, 일반적인 생각과는 달리 이들 플랫폼의 비정렬 액세스는 오랫동안 비정렬 액세스와 거의 같은 속도입니다(정렬 액세스 속도가 훨씬 느리다는 것은 없습니다).유일한 단점은 원자력이 아닐 수도 있다는 것입니다만, 이 경우는 문제가 되지 않는다고 생각합니다.또한 컴파일러 간에 합의가 이루어지며, 패킹된 구조는 동일한 레이아웃을 사용합니다.

GCC/clang은 말씀하신 구문을 사용하여 패킹된 구조를 지원합니다.MSVC에는#pragma pack다음과 같이 사용할 수 있습니다.

#pragma pack(push, 1)
struct Sensor1Telemetry {
    int16_t temperature;
    uint32_t timestamp;
    uint16_t voltageMv;
    // etc...
};
#pragma pack(pop)

다음 두 가지 문제가 발생할 수 있습니다.

  1. 엔디안은 플랫폼 간에 동일해야 합니다(MCU는 리틀 엔디안을 사용해야 합니다).
  2. 패킹된 구조 멤버에 포인터를 할당하고 정렬되지 않은 액세스를 지원하지 않는 아키텍처에 있는 경우(또는 다음과 같은 정렬 요구 사항이 있는 명령을 사용함)movaps또는ldrd이 경우 해당 포인터를 사용하여 크래시가 발생할 수 있습니다(gcc는 경고하지 않지만 clang은 경고합니다.

다음은 GCC의 문서입니다.

패킹된 속성은 변수 또는 구조 필드의 정렬이 가능한 최소 크기(변수의 경우 1바이트)여야 함을 지정합니다.

그래서 GCC보장은 없패딩 사용될 것이다.

MSVC:

수업을 우산도 못 챙기고 서로 직접 후 기억에 있는 회원들을 배치하는 것이다.

그래서 MSVC보장은 없패딩 사용될 것이다.

bitfields의 제가 발견한 유일하게"위험한"지역이 사용.그리고 레이아웃 GCC와 MSVC사이에 달라질 수 있다.하지만 GCC에서 그들 호환되므로 선택 사항: 있다.-mms-bitfields


조언:만약 이 이제 해결책이며, 또 그것이 작동을 멈출 것 같지는 않다 심지어, 나는 당신이 이 해결책에서 당신의 코드의 의존하는 것이 좋습니다.

노트:나는 이 대답에 오직 GCCclang과 MSVC생각해 봤습니다.에는 컴파일러는 아마도, 이런 것들을 가능하지 못하고 있다.

한다면

  • Endianness는 문제가 아니다.
  • 둘 다 컴파일러를 올바르게 포장하고 있다.
  • 둘 다 C구현에 관한 그 형식 정의가 정확한(컴플라이언스 표준).

그리고 네,"구조 만원"휴대할 수 있다.

내 맛을 위해서는 너무 많은"만약"s, 이건 하지 마그것은 그 귀찮은 일이 발생할 가치가 없어요.

그렇게 할 수도 있고, 더 신뢰할 수 있는 대안을 사용할 수도 있습니다.

연재 광신도들의 핵심에는 캡앤프로토가 있습니다.이것에 의해, 네트워크상에서 전송 해 간단하게 작업할 수 있는 네이티브한 구조를 얻을 수 있습니다.또, 다른 쪽에서도 대응할 수 있도록 합니다.그것을 연속화라고 부르는 것은 거의 부정확하다; 그것은 구조의 기억 속 표현을 가능한 한 조금 하는 것을 목표로 한다.M4로의 포팅에 적합할 수 있습니다.

Google Protocol Buffers가 있습니다. 2진법이죠.좀 더 부풀어 올랐지만 꽤 괜찮았어부수되는 nanopb(마이크로컨트롤러에 더 적합)가 있지만 GPB 전체를 실행하는 것은 아닙니다(그렇다고는 생각하지 않습니다).oneof많은 사람들이 성공적으로 사용하고 있습니다.

C asn1의 일부 런타임은 마이크로 컨트롤러에서 사용할 수 있을 정도로 짧습니다.이게 M0에 맞는 거 알아

휴대성이 뛰어난 것을 원하는 경우는, 다음의 버퍼를 선언할 수 있습니다.uint8_t[TELEM1_SIZE]그리고.memcpy()다음과 같은 엔디안니스 변환을 수행하면서 그 안에서 오프셋을 주고받습니다.htons()그리고.htonl()(또는 glib와 같은 little-endian 등가물).이를 클래스에서 C++로 getter/setter 메서드를 사용하거나 C로 getter-setter 함수를 가진 구조로 래핑할 수 있습니다.

대체방법에 대해 말씀드리면, (평가가 충분하지 않은) 데이터 을 위한 Tuple과 같은 컨테이너에 대해 생각해 보면, Alex Robenko의 CommsChampion 프로젝트를 보시기 바랍니다.

COMMS는 C++(11) 헤더 전용 플랫폼 독립 라이브러리이며, 통신 프로토콜의 구현을 쉽고 비교적 빠른 프로세스로 만듭니다.이 명령어는 사용자 지정 메시지를 정의하는 데 필요한 모든 유형과 클래스를 제공하며 전송 데이터 필드를 래핑하여 유형 및 클래스 정의의 단순한 선언문으로 만듭니다.이러한 문장은 구현해야 할 사항을 명시합니다.COMMS 라이브러리 내부에서는 HOW 부분을 처리합니다.

Cortex-M4 마이크로컨트롤러에서 작업하고 있기 때문에 다음과 같은 흥미로운 점도 발견할 수 있습니다.

COMMS 라이브러리는 베어메탈을 포함한 임베디드 시스템에서 사용하도록 특별히 개발되었습니다.예외 및/또는 RTTI를 사용하지 않습니다.또한 동적 메모리 할당 사용을 최소화하고 필요에 따라 완전히 제외할 수 있습니다. 베어메탈 임베디드 시스템을 개발할 때 필요할 수 있습니다.

Alex는 C++(임베디드 시스템용)에서의 통신 프로토콜 구현 가이드라는 제목의 우수한 전자책을 무료로 제공하고 있습니다.이 책에는 내부 정보가 기재되어 있습니다.

C++는 어떤 구조인지에 따라 크게 달라집니다.struct는 기본 가시성이 public인 클래스입니다.

이를 통해 가상 환경을 상속하고 추가할 수 있으므로 문제가 발생할 수 있습니다.

순수 데이터 클래스(C++ 용어로 표준 레이아웃 클래스)인 경우 이 클래스와 함께 작동해야 합니다.packed.

또, 이것을 개시하면, 컴파일러의 엄밀한 에일리어스 룰에 문제가 생길 가능성이 있습니다.메모리의 바이트 표현을 조사할 필요가 있기 때문입니다.-fno-strict-aliasing당신의 친구입니다.)

메모

그래서 나는 그것을 연재하는 데 사용하지 말 것을 강력히 권고한다.툴(프로토부프, 플랫버퍼, msgpack 등)을 사용하면 다음과 같은 기능을 많이 얻을 수 있습니다.

  • 언어의 독립성
  • rpc(리모트프로시저 콜)
  • 데이터 사양 언어
  • 스키마/검증
  • 버전 관리

다음은 적절한 타깃 OS 및 플랫폼에서의 사용을 보증하기 위해 필요한 알고리즘에 대한 의사 코드입니다.

를 사용하는 경우C사용할 수 없는 언어classes,templates 외에 몇 , '하다'는 말을 쓸 수 요.preprocessor directives.struct(s)한 것은 ''을 기반으로 하고 있습니다.OS, 설계자, 설계자CPU-GPU-Hardware Controller Manufacturer {Intel, AMD, IBM, Apple, etc.},platform x86 - x64 bit 「」를 참조해 주세요.endian이치노그렇지 않으면 C++와 템플릿 사용에 초점을 맞출 수 있습니다.

struct(s)예를 들어 다음과 같습니다.

struct Sensor1Telemetry {
    int16_t temperature;
    uint32_t timestamp;
    uint16_t voltageMv;
    // etc...
} __attribute__((__packed__));

struct TelemetryPacket {
    Sensor1Telemetry tele1;
    Sensor2Telemetry tele2;
    // etc...
} __attribute__((__packed__));

다음과 같은 구조물을 템플릿으로 만들 수 있습니다.

enum OS_Type {
    // Flag Bits - Windows First 4bits
    WINDOWS    = 0x01  //  1
    WINDOWS_7  = 0x02  //  2 
    WINDOWS_8  = 0x04, //  4
    WINDOWS_10 = 0x08, //  8

    // Flag Bits - Linux Second 4bits
    LINUX      = 0x10, // 16
    LINUX_vA   = 0x20, // 32
    LINUX_vB   = 0x40, // 64
    LINUX_vC   = 0x80, // 128

    // Flag Bits - Linux Third Byte
    OS         = 0x100, // 256
    OS_vA      = 0x200, // 512
    OS_vB      = 0x400, // 1024
    OS_vC      = 0x800  // 2048

    //....
};

enum ArchitectureType {
    ANDROID = 0x01
    AMD     = 0x02,
    ASUS    = 0x04,
    NVIDIA  = 0x08,
    IBM     = 0x10,
    INTEL   = 0x20,
    MOTOROALA = 0x40,
    //...
};

enum PlatformType {
    X86 = 0x01,
    X64 = 0x02,
    // Legacy - Deprecated Models
    X32 = 0x04,
    X16 = 0x08,
    // ... etc.
};

enum EndianType {
    LITTLE = 0x01,
    BIG    = 0x02,
    MIXED  = 0x04,
    // ....
};

// Struct to hold the target machines properties & attributes: add this to your existing struct.

struct TargetMachine {
    unsigned int os_;
    unsigned int architecture_;
    unsigned char platform_;
    unsigned char endian_;

    TargetMachine() : 
      os_(0), architecture_(0),
      platform_(0), endian_(0) {
    }

    TargetMachine( unsigned int os, unsigned int architecture_, 
                   unsigned char platform_, unsigned char endian_ ) :
      os_(os), architecture_(architecture),
      platform_(platform), endian_(endian) {
    }    
};

template<unsigned int OS, unsigned int Architecture, unsigned char Platform, unsigned char Endian>
struct Sensor1Telemetry {       
    int16_t temperature;
    uint32_t timestamp;
    uint16_t voltageMv;
    // etc...
} __attribute__((__packed__));

template<unsigned int OS, unsigned int Architecture, unsigned char Platform, unsigned char Endian>
struct TelemetryPacket {
    TargetMachine targetMachine { OS, Architecture, Platform, Endian };
    Sensor1Telemetry tele1;
    Sensor2Telemetry tele2;
    // etc...
} __attribute__((__packed__));

이것으로enum할 수 class template specialization class위의 조합에 따라 필요에 따라 달라집니다.에서는 잘 될 것 를 모두 .default class declaration & definition그것을 메인 클래스의 기능으로 설정합니다. 그런 '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다', '다르다',Endian순서 이 다른 하고 있는 또는 OS 버전이 있는 경우GCC versus MS를 한 컴파일러__attribute__((__packed__))vs 대 #pragma pack()설명해야 할 몇 가지 전문 분야가 될 수 있습니다.가능한 모든 조합에 대해 전문화를 지정할 필요는 없습니다.그것은 매우 번거롭고 시간이 걸리는 몇 가지 드문 경우 시나리오만 실시하면 대상 독자에게 항상 적절한 코드 지침을 제공할 수 있습니다., 「 」가 은 무엇입니까?enums함수 인수로 전달하면 비트플래그로 설계되어 있기 때문에 한 번에 여러 개의 인수를 설정할 수 있습니다.따라서 이 템플릿 구조를 첫 번째 인수로 사용하고 지원되는 OS를 두 번째 인수로 사용하는 함수를 만들고 싶다면 사용 가능한 모든 OS 지원을 비트플래그로 전달할 수 있습니다.

은, 「」의 세트를 하는 데 이 될 가능성이 있습니다.packed structures는 적절한 타겟에 따라 올바르게 "패킹"되어 있으며, 다른 플랫폼 간에 이동성을 유지하기 위해 항상 동일한 기능을 수행합니다.

다른 지원 컴파일러용 프리프로세서 디렉티브 간에 이 전문화를 2회 실시할 필요가 있습니다.따라서 현재 컴파일러가 GCC인 경우, 그 전문성을 가진 구조를 한 가지 방법으로 정의하는 경우, 다른 방법으로 Clang in the or MSVC, Code Blocks 등을 수행합니다.따라서 초기 설정에는 약간의 오버헤드가 있지만 타깃 머신의 지정된 시나리오 또는 속성 조합에서 적절하게 사용되고 있는지 확인할 수 있습니다.

항상은 아냐.다른 아키텍처 프로세서로 데이터를 전송할 때는 Endianness, 원시 데이터 유형 등을 고려해야 합니다.알뜰 또는 메시지 팩을 사용하는 것이 좋습니다.그렇지 않은 경우 Serialize 메서드와 DeSerialize 메서드를 직접 만듭니다.

컴파일 도메인 전체나 메모리(하드웨어 레지스터, 파일에서 읽은 항목 선택, 프로세서 간 또는 동일한 프로세서 간 다른 소프트웨어 간(앱과 커널 드라이버 간) 데이터 전달)에 걸쳐 구조를 사용하지 마십시오.컴파일러는 얼라인먼트를 자유롭게 선택할 수 있고, 그 위에 있는 사용자가 수식어를 사용함으로써 상황을 악화시킬 수 있기 때문에 문제가 발생합니다.

예를 들어 다른 타깃(컴파일러의 다른 빌드 및 타깃 차이)에 대해 동일한 gcc 컴파일러 버전을 사용하는 경우에도 플랫폼 전체에서 이를 안전하게 수행할 수 있다고 가정할 이유는 없습니다.

실패 확률을 낮추려면 먼저 가장 큰 항목부터 시작합니다(64비트, 16비트, 마지막으로 8비트 항목).이상적으로는 최소 32개로 정렬합니다.아마도 x86이 원하는 대로라면 언제든지 변경할 수 있습니다.기본값은 소스로부터 컴파일러를 빌드하는 사람도 변경할 수 있습니다.

작업보안에 관한 것이라면 이 코드에 대해 정기적인 유지보수를 실시할 수 있습니다.각 타겟의 구조에 대한 정의가 필요할 가능성이 높습니다(ARM의 구조 정의에 대한 소스 코드의 복사본과 x86의 소스 코드의 복사본이 각각 필요합니다.즉시 필요하지는 않습니다).그리고 제품 출시 때마다 코드 작업을 위해 호출을 받습니다.유지보수의 시한폭탄이 터지고...

동일하거나 다른 아키텍처로 컴파일 도메인 또는 프로세서 간에 안전하게 통신하려면 크기, 바이트 스트림, 하프워드 스트림 또는 워드 스트림을 사용합니다.장차 장애 및 유지보수의 위험을 크게 줄일 수 있습니다.구조물을 사용하여 위험과 실패만 복구하는 항목을 분리하지 마십시오.

같은 타깃 또는 패밀리(또는 다른 컴파일러 선택에서 파생된 컴파일러)에 대해 같은 컴파일러 또는 패밀리를 사용하는 것이 좋다고 생각하는 이유는 언어의 규칙과 구현 정의 영역이 어느 정도인지 이해하고 있기 때문에 결과적으로 차이가 발생할 수 있으며, 경우에 따라서는 수십 년이 걸릴 수 있습니다.가끔 몇 주가 걸리기도 하고"내 기계로 작동" 문제예요

언급URL : https://stackoverflow.com/questions/45116212/are-packed-structs-portable

반응형