Verwendung der Compiler-Direktive #pragma pack(1) für eingebettete Anwendungen

Ich bin kürzlich auf diese Präprozessordirektive #pragma pack(1) gestoßen und habe mich gefragt, warum sie verwendet wird.

Ich habe die Verwendung gegoogelt und festgestellt, dass es andere Optionen wie Push, Pop usw. gibt. Hat jemand dies in seiner eingebetteten Anwendung verwendet?

Ich würde gerne einige Beispiele dafür erfahren, wie/warum Sie diese Richtlinie verwendet haben und auf welche Art von Auftragsverarbeiter? Was sind die Vor- und Nachteile der Verwendung dieser Richtlinie?

Danke.

Nur etwas, das Sie im Hinterkopf behalten sollten: Pragmas sind per Definition nicht standardmäßig und die verfügbaren Pragmas und ihre Verwendung unterscheiden sich je nachdem, welchen Compiler Sie verwenden. Die für diese spezielle Anwendung gelernten Lektionen lassen sich NICHT unbedingt auf eine andere Situation übertragen.

Antworten (3)

#pragma pack(1)stellt sicher, dass C struct-Elemente der Reihe nach und auf Byte-Grenzen gepackt werden. Es kann RAM sparen - was in einem Mikrocontroller oft wertvoll ist.

Gepackte Strukturen ermöglichen auch das Casting direkt über Speicherpuffer für den Datenaustausch:

void f(void *buf)
{
  struct ip_header *hdr = (struct ip_header *)buf;
  hdr->dst = 0x8000001;
}

Seien Sie vorsichtig, wo Sie verwenden #pragma pack. Es hat einen globalen Bereich (wie es im Präprozessor ist), also wirkt sich das Vergessen, es auszuschalten, auf alle #includeDateien aus. Wenn Sie nur bestimmte Strukturen packen möchten, sollten Sie die Verwendung von GCC in Betracht ziehen __attribute__ ((packed)).

Aufgrund von Ausrichtungsproblemen können gepackte Strukturen jedoch die Leistung beeinträchtigen. Zum Beispiel:

Wenn in Bytes gepackt:

struct
{
  uint8_t  a;
  uint32_t b;
  uint8_t  c;
  uint8_t  d:
};

Wird gespeichert als (ohne Berücksichtigung der Endianness):

a, b0, b1, b2, b3, c, d

Viele Architekturen erfordern jedoch, dass 32-Bit-Zugriffe auf 32-Bit-Adressen ausgerichtet werden. Mit der gepackten Struktur muss die Maschine mehrere Byte-Zugriffe durchführen und sie dann wieder zusammenfügen.

Konfrontiert mit dem oben structGesagten ohne aktiviertes Packen, könnte der Compiler es reorganisieren als:

b0, b1, b2, b3, a, c, d

In Ihrem letzten Beispiel könnten einige Compiler ohne aktiviertes Packen und eine standardmäßige 32-Bit-Ausrichtung stattdessen zusätzliche Bytes zur Struktur hinzufügen, sodass sie im Speicher als a, x, x, x, b0, b1, b2, b3 erscheint , c, x, x, x, d wobei x ein Füllbyte ist. Andere Variationen sind möglich.
Vielen Dank für Ihre Erklärung, Joby und Tcrosley. Ich verstehe es endlich!
Es ist wichtig, dass Sie es ausschalten, indem Sie es auf 0 setzen, nachdem Sie fertig sind. Wenn Sie dies nicht tun, führt dies zu einem seltsamen Verhalten.
Compiler dürfen die Reihenfolge der Felder in C-Strukturen nicht ändern. Sie könnten selbst neu anordnen, um b an die erste Stelle zu setzen.

Ein weiterer Grund, Strukturen an Byte-Grenzen zu packen, besteht darin, die Ausrichtung der Mitglieder sicherzustellen, wenn Daten zwischen verschiedenen Prozessoren übertragen werden.

Ich muss häufig Datenstrukturen zwischen einer MCU und einer Host-PC-Anwendung übertragen. Der PC packt Strukturen auf 32-Bit-Grenzen, wenn er nicht angewiesen wird, sie auf 1-Byte-Grenzen zu packen. Eine PIC24F-MCU packt Strukturen auf 16-Bit-Grenzen, es sei denn, sie werden angewiesen, sie auf 1-Byte-Grenzen zu packen.

Indem beide angewiesen werden, ihre Strukturen auf 1-Byte-Grenzen zu packen, wird sichergestellt, dass sich die Daten beim Zugriff an beiden Enden an derselben Stelle befinden. Ohne sie müssten Sie die Strukturen mit Reservebytes auffüllen, damit die Datenmitglieder richtig ausgerichtet werden.

Die traditionelle Verwendung, bei der ich dies gesehen habe, ist das Lesen von Informationen aus Dateien. Beispielsweise können Sie eine Struktur definieren, deren Mitglieder mit denen eines BMP-Dateiheaders übereinstimmen, und dann den gesamten Header in einem schnellen Lesevorgang lesen. OK, BMP ist vielleicht nicht das beste Beispiel (sein Header hat keine Ausrichtungsprobleme auf 32-Bit-Systemen), aber Sie haben die Idee. Ich nehme an, dass dies in der eingebetteten Welt genauso nützlich ist.

In der Tat. Sie können eine Struktur auf ein eingehendes Paket legen (oder es verwenden, um ein ausgehendes Paket zu erstellen), auf einen Satz von speicherabgebildeten Registern oder ein Protokollpaket erstellen, alles Fälle, in denen das Packen möglicherweise von der optimalen Ausrichtung der CPU für Speicherzugriffe abweichen könnte .
Ganz richtig, aber Vorsicht vor Endianness. Wenn Multi-Byte-Entitäten auf eine Struktur abgebildet werden, müssen sie möglicherweise Endian-Austauschen. Zum Beispiel sind IP-Pakete Big-Endian gepackt, aber x86 und (normalerweise) ARM sind Little-Endian