GCC構造体内のメモリアラインメント
-
23-09-2019 - |
質問
私はCのARMプラットフォームにアプリケーションを移植しています。アプリケーションはX86プロセッサでも実行されており、後方互換性がなければなりません。
私は今、可変アライメントに関するいくつかの問題を抱えています。 GCCマニュアルを読みました__attribute__((aligned(4),packed))
私は、構造体の開始が4バイトの境界に整合し、内部が詰め込まれたステートメントのために触れられないままであるため、言われていることを解釈します。
もともと私はこれを持っていましたが、時々それは4バイトの境界で整列されていません。
typedef struct
{
unsigned int code;
unsigned int length;
unsigned int seq;
unsigned int request;
unsigned char nonce[16];
unsigned short crc;
} __attribute__((packed)) CHALLENGE;
だから私はそれに変えます。
typedef struct
{
unsigned int code;
unsigned int length;
unsigned int seq;
unsigned int request;
unsigned char nonce[16];
unsigned short crc;
} __attribute__((aligned(4),packed)) CHALLENGE;
以前に述べた理解は、両方の構造体が4バイトの境界に整列され、内側のデータが4バイトの境界に整列するため、間違っているように見えますが、終わりのために、構造体のサイズが増加しています。 42〜44バイトのサイズ。このサイズは、構造体が42バイトであることに依存する他のアプリケーションがあるため、重要です。
私が必要とする操作をどのように実行するかを私に説明する人もいますか。どんな助けも大歓迎です。
解決
あなたが依存している場合 sizeof(yourstruct)
42バイトであるため、あなたは携帯性のない仮定の世界に噛まれようとしています。これが何のためであるかは言っていませんが、構造体の内容の終わりも重要であるように思われるので、x86でもミスマッチがあるかもしれません。
この状況では、対処する唯一の確実な方法は使用することだと思います unsigned char[42]
それが重要な部分で。この42バイトブロックのどこにいるか、どのEndianがどのフィールドであるかを正確に指定し、その定義を使用して、対話できる構造との間に翻訳するコードを記述します。このコードは、すべてのオールアットシリアル化コード(別名マーシャリング)、または多くのゲッターとセッターのいずれかである可能性があります。
他のヒント
これが、メンバーワイズの代わりに完全な構造を読むことが失敗し、避けるべき理由の1つです。
この場合、パッキングプラス4で調整することは、2バイトのパディングがあることを意味します。これは、すべてのアイテムが4で並べられているアレイにタイプを保存するためにサイズが互換性がある必要があるために発生します。
私はあなたが次のようなものを持っていると思います:
read(fd, &obj, sizeof obj)
さまざまなデータに属する2つのパディングバイトを読みたくないため、サイズを明示的に指定する必要があります。
read(fd, &obj, 42)
あなたが維持可能に保つことができます:
typedef struct {
//...
enum { read_size = 42 };
} __attribute__((aligned(4),packed)) CHALLENGE;
// ...
read(fd, &obj, obj.read_size)
または、CでC ++のいくつかの機能を使用できない場合:
typedef struct {
//...
} __attribute__((aligned(4),packed)) CHALLENGE;
enum { CHALLENGE_read_size = 42 };
// ...
read(fd, &obj, CHALLENGE_read_size)
次のリファクタリングの機会に、各メンバーを個別に読み始めることを強くお勧めします。これは、機能内で簡単にカプセル化できます。
あなたの本当の目標は何ですか?
特定の形式のファイルまたはワイヤー上にあるデータを処理する場合は、あなたがすべきことは、あなたがあなたが内部のデータへの対処方法を表すコンパイラ構造体間でデータを移動するいくつかのマーシャリング/シリアル化ルーチンを書き上げることですプログラムと、ワイヤー/ファイルのデータがどのように見えるかを扱うcharアレイ。
その場合、慎重に対処する必要があり、場合によってはプラットフォーム固有のコードを使用する必要があります。これは、マーシャリングルーチンです。また、マーシャリングされたデータが、今日および将来どのプラットフォームに移植する必要があるかに関係なく、マーシャリングされたデータが構造体に適切に到達することを保証するために、いくつかの素敵なnastのユニットテストを作成できます。
Linux、Windows、Mac、C、Swift、Assemblyなどから構造を行き来しています。
問題は、それができないということではなく、問題は怠け者ではなく、ツールを理解しなければならないということです。
なぜあなたが使用できないのかわかりません:
typedef struct
{
unsigned int code;
unsigned int length;
unsigned int seq;
unsigned int request;
unsigned char nonce[16];
unsigned short crc;
} __attribute__((packed)) CHALLENGE;
君は できる それを使用すると、特別なコードや賢いコードは必要ありません。私は腕に通信する多くのコードを書きます。構造は、物事を機能させるものです。 __attribute__ ((packed))
私の友達です。
あなたが両方で何が起こっているのかを理解していれば、「傷の世界」にいる確率はゼロです。
最後に、私はあなたが42または44を得る方法を人生のために作ることはできません。Intは4つの8バイト(コンパイラに応じて)のいずれかです。これにより、数値は16+16+2 = 34または32+16+2 = 50になります。
私が言うように、あなたのツールを知ることはあなたの問題の一部です。
問題は42が4で割り切れないので、これらの構造体のいくつかを背中合わせに置くと、それらはアライメントから抜け出すことだと思います(例えば、それらのいくつかにメモリを割り当て、サイズを決定します sizeof
)。あなたが要求したように、これらのケースでは44のサイズを持つことは、これらの場合にアライメントを強制します。ただし、各structメンバーの内部オフセットが同じままである場合、44バイト構造体を42バイトのように扱うことができます(正しい境界で次のデータを整列するように注意する限り)。
試してみるのに1つのトリックが置くことかもしれません 両方 単一の組合タイプ内のこれらの構造体のうち、そのような各組合内の42バイトバージョンのみを使用します。
Linuxを使用しているので、 echo 3 > /proc/cpu/alignment
警告が発行され、アライメントの問題が修正されます。これは回避策ですが、構造が整列されていない場所を見つけるのに非常に役立ちます。