Angenommen, ich verwende einen 8051. Ich habe einen Code kompiliert, der eine statische Variable enthält. Wo wird diese statische Variable gespeichert? Im RAM? Im Stapel? Auf dem Haufen? In Flash? Wo?
Korrigieren Sie mich auch, wenn ich falsch liege: CONST werden im ROM gespeichert. Und globale Variablen werden in Flash gespeichert. (Was ist, wenn ich kein Flash habe?)
Ich erwarte Antworten in Bezug auf Embedded Programming. Es ist nicht wie im Codesegment oder in .bss, aber genau wo auf meinem Board?
Antworten:
Die folgende Antwort basiert auf meiner Erfahrung mit Kartendateien, wenn ich mich in Bezug auf etw irre. Bitte korrigiere mich!
Statische Variablen werden definitiv nicht auf dem Heap gespeichert, da dies nur für Variablen gilt, die zur Laufzeit zugewiesen werden (und statische Variablen werden während der Kompilierungszeit zugewiesen).
Statische Variablen werden genau wie Ihre globalen Variablen im RAM gespeichert. Der Umfang einer bestimmten Variablen ist nur für den Compiler von Bedeutung. Auf der Ebene des Maschinencodes hindert Sie niemand daran, eine lokale Variable außerhalb einer Funktion zu lesen (solange Ihr Controller nicht über einige ausgefallene Funktionen verfügt, um den Zugriff auf Speicherbereiche zu verhindern). .
Denken Sie darüber nach: Um ein einzelnes Flash-Bit zu schreiben, müssen Sie einen ganzen Datenblock löschen und dann das Ganze mit den geänderten Daten neu schreiben. Und diese Schritte werden nicht in einem einzigen Zyklus ausgeführt, wie dies bei einem einfachen Speichern im RAM der Fall ist. Globale Variablen werden wie bereits erwähnt im RAM gespeichert. Dies löst auch Ihre Verwirrung über Systeme ohne Blitz.
quelle
const
Schlüsselwort nutzlos wäre?Dann sollten Sie sich mit CODE-, DATA-, IDATA-, XDATA- und PDATA-Speicher auskennen - 8051 ist eine Multi-Harvard-Architektur.
Das ist eine gute Frage. Dies hängt von den Compilereinstellungen ab - normalerweise als "Speichermodell" bezeichnet. Sie können aber auch explizit sagen, wo der Compiler es ablegen wird:
Der Compiler / Linker sollte in der Lage sein, eine Map-Datei zu erstellen, in der die Adressen Ihrer Variablen angezeigt werden.
Dies hängt wahrscheinlich vom Compiler ab und muss mit der Map-Datei verglichen werden. Ich erinnere mich, dass ich diese explizit markiert habe:
Sowohl wahr als auch falsch. Sie befinden sich in einem der Datenspeicher des 8051, aber der Anfangswert wird beim Start aus Flash geladen - es sei denn, die Variable wird mit Null initialisiert.
quelle
Auf Prozessoren, bei denen sich der Codespeicher im selben Adressraum befindet wie alle anderen Variablen, platzieren Compiler normalerweise "const" -qualifizierte globale oder statische Variablen in ihrem eigenen Linkabschnitt, und Linker werden normalerweise so konfiguriert, dass dieser Abschnitt im Code des Systems platziert wird speichern (Flash, OTP oder was auch immer). Dies reduziert den vom Programm benötigten Arbeitsspeicher und den Arbeitsaufwand für den Startcode.
Auf Prozessoren, bei denen sich der Codespeicher in einem anderen Adressraum befindet (z. B. PIC oder 8051), verwenden einige Compiler ein
const
Qualifikationsmerkmal, um zu signalisieren, dass sie die Variablen in den Codespeicher einfügen und unterschiedliche Anweisungen verwenden sollen, um auf sie zuzugreifen, während andere dies nicht tun . Solche Compiler erfordern, dass nur Zeiger mit einemconst
Qualifizierer für den Zugriff aufconst
deklarierte Variablen verwendet werden dürfen, da die Compiler ohne diese Anforderung nicht wissen würden, dass spezielle Anweisungen für den Zugriff auf solche Zeiger verwendet werden müssen.Auf den 8051-Compilern, die ich gesehen habe (Archimedes und Keil), sind 8051-Compilerspezifische Schlüsselwörter __data, __idata, __code, __bdata, _pdata und _xdata verfügbar, um anzugeben, dass Variablen in einen bestimmten Adressraum geladen werden sollen. Standardmäßig können die Namen mit oder ohne Unterstriche verwendet werden. Die nicht unterstrichenen Versionen sind praktischer, können jedoch deaktiviert werden, wenn beispielsweise ein Programm portiert wird, das Bezeichner mit dem Namen
code
oderdata
) verwendet. Wenn ein Zeiger deklariert wird, ohne eines dieser Schlüsselwörter auf sein Ziel anzuwenden, weist der Compiler drei Bytes zu: eines, um anzugeben, in welchem Speicherplatz sich das Ziel befindet, und zwei, um eine 16-Bit-Adresse zu speichern, falls eines erforderlich ist. Deklarieren einer Variablenconst
ohne Anwenden von acode
Durch das Qualifikationsmerkmal wird die Variable in den Standard-RAM-Adressraum gestellt und beim Start mit dem Standardwert geladen. Eine auf diese Weise deklarierte Variable kann an Code übergeben werden, der einen Zeiger im Standardadressraum erwartet (verwendet jedoch RAM). Durch Hinzufügen einer__code
(odercode
, falls aktiviert) Deklaration wird die Variable im Codebereich platziert. Es ist normalerweise besser, diecode
Deklaration zu verwenden als nicht, aber in einigen Fällen, insbesondere wenn das betreffende Element klein ist, kann der vergleichsweise einfache Zugriff auf Dinge imidata
RAM den Verlust einiger Bytes dieses Speicherplatzes ausgleichen.quelle
Teilantwort: "Korrigieren Sie mich auch, wenn ich falsch liege: CONST werden im ROM gespeichert."
Falsch. Das Schlüsselwort 'const' ist eine Anweisung, die dem Compiler helfen soll. Wenn eine Variable als 'const' deklariert wird, weiß der Compiler, dass sie sich nicht ändern kann, aber es ist immer noch eine Variable, der ein Speicherort zugewiesen ist.
Wenn Sie es im ROM speichern möchten / möchten, müssen Sie ein anderes Schlüsselwort für den Compiler verwenden, das angibt, welcher Speichertyp oder Speicherort verwendet werden soll. Ich glaube nicht, dass es dafür ein standardisiertes Schlüsselwort gibt. Wahrscheinlich, weil C auf einem PC begann, bei dem der gesamte Programmraum und der variable Raum RAM waren.
Für einen PIC würden Sie das Schlüsselwort 'rom' verwenden, um die Variable zu deklarieren.
zB "rom int const x = 42;"
quelle
Statisch bedeutet, dass die Variable sowohl für diese Datei / Funktion / diesen Codebereich isoliert ist. Der Ort, an dem es definiert ist, ist sowohl nur dort sichtbar als auch bei Verwendung auf Nicht-Globalen wird diese Variable aus Speichersicht im Wesentlichen zu einer globalen Variablen. Eine lokale Variable mit einer statischen Definition bedeutet, dass ich diesen Wert nicht von einem Aufruf dieser Funktion an einen anderen verlieren möchte. Daher muss er für diese Funktion global gespeichert werden. Normalerweise befindet sich eine lokale Variable auf dem Stapel, damit die Funktion erneut ausgeführt werden kann Teilnehmer (eine neue Kopie der lokalen Variablen für jeden Eintrag in die Funktion). Statische Globale sind also sowieso global, statische Lokale sind lokal, werden aber zusammen mit den Globalen gespeichert. In so genannten .data können Toolchains jedoch unterschiedliche Namen haben.
sind gelagert. Globale Variablen oder statische Lokale, die bei der Definition nicht initialisiert werden
sind in dem, was oft .bss genannt wird. Dies ist ein weiterer Teil des Lese- / Schreibspeichers, aber dieser Speicher vor dem Start von main () wird für Sie auf Null gesetzt, sodass diese Variablen gemäß der Spezifikation oder Annahme beim Starten Ihres Programms Null sind.
const ist eine Art zu sagen, dass ich dies als schreibgeschützte Variable deklariere. Ich plane nicht, dass jeder Speicher darauf nur gelesen wird. Der Compiler kann also wählen (tatsächlich ist es der Programmierer, der letztendlich vorschreibt, was der Linker tut, wobei häufig das Standard-Linker-Skript verwendet wird und dieser Job nicht übernommen wird), ob dies auf Flash oder RAM geht. Er kann entweder an einen Ort gehen und funktionieren Alles gut.
Für Mikrocontroller und andere Orte, an denen Sie booten und aus dem nichtflüchtigen Speicher (Flash / Rom / usw.) starten müssen. Zunächst muss das Programm selbst in etwas nichtflüchtigem gespeichert werden. Als nächstes das .data-Zeug, Dinge, die Sie in Ihrem Code beim Definieren der Variablen initialisiert haben, Dinge, die zur Kompilierungszeit bestimmt werden können. muss sich auch im nichtflüchtigen Speicher befinden, aber diese Daten werden letztendlich gelesen / geschrieben, damit der Bootstrap-Code, der vor main () ausgeführt wird, die .data-Blöcke in den Lese- / Schreibspeicher kopiert. Der .bss-Code oder globale oder statische lokale Variablen, die nicht initialisiert werden und beim Start als Null angenommen werden, benötigen keinen nichtflüchtigen Speicher, nur den Speicherort und wie viel, und daraus kann der Bootstrap den Wert / lesen. Speicher schreiben.
Es gibt Gründe, warum wir mit Begriffen wie .text, .data, .bss, .rodata usw. kommunizieren. Weil wir sehen, dass die Tools Elemente unseres Programms an diesen Orten platzieren und dann sehen können, wo diese Orte in Nicht-Orten leben müssen -flüchtiger Speicher und dann zur Laufzeit, wenn das anders ist. .text obwohl doof, ist unser Programm, der Maschinencode und andere zugehörige Daten. .data sind Variablen aus unserem Programm, die vor dem Beginn ungleich Null initialisiert werden. Sie müssen daher in einem nichtflüchtigen Speicher gespeichert und dann vor ihrer Verwendung zum Lesen / Schreiben verschoben werden. .bss sind Variablen aus unserem Programm, die beim Start als Null angenommen werden. Daher müssen Menge und Position nichtflüchtig gespeichert werden, und der Boostrap kann die Nullung durchführen (oder manchmal werden sie nur als eine Reihe von Nullen in gespeichert das Flash / Rom auch). .
Dann gibt es die Begriffe Heap und Stack, die bei Bedarf häufig nach Ihrem Programm .bss und .data eine gewisse Menge RAM verbrauchen, häufig von den unteren Adressen aufwärts. dann wird der Rest dieses RAMs in Heap und Stack aufgeteilt, manchmal ohne eine durchgezogene Linie zwischen ihnen. Bei schlecht benommenen Programmen können Heap und Stack kollidieren und einen Absturz verursachen. Stapel ist oft von oben nach unten und Haufen ist oft von unten nach oben, aber dies sind keine festen Regeln.
Wo sind diese auf deinem Board? Nun, es hängt sowohl vom Chip als auch vom Board ab, welche Optionen es gibt. Letztendlich liegt es an Ihnen, dem Programmierer, zu entscheiden, wie Sie den Kompilierungs- und Verknüpfungsprozess steuern. Die meisten Leute erhalten ein Beispiel oder eine Toolchain, die sich mit Ihrem System auskennt, und Sie belassen die Standardeinstellungen. Aber Sie sind äußerst verantwortlich. Damit ein Mikrocontroller booten kann, muss dies möglich sein. In der Regel handelt es sich dabei je nach MCU um einen Flash-On- oder Off-Chip. Das oben genannte nichtflüchtige Material muss dort oder an einem anderen nichtflüchtigen Ort aufbewahrt werden . Ebenso haben Sie einen RAM, den Sie mit den Lese- / Schreib-Dingen aufteilen müssen, die Sie benötigen, einschließlich Stapel und Heap, wenn Sie mutig genug sind, ihn in einem solchen System zu verwenden (nicht weise). Bei einigen Prozessoren ist der Stack Teil des Designs und Sie müssen sich keine Sorgen machen, andere tust du. Wenn Sie ein Vielfaches dieser Dinge haben, nichtflüchtig oder Lese- / Schreib-RAM, können Sie auswählen, wo die Dinge abgelegt werden sollen.
Wenn Sie eine Toolchain von jemandem verwenden oder Zugriff auf andere haben, gibt es keinen Grund anzunehmen, dass diese mit ihrem Programm mit ihrem Standard-Linker-Skript genau dasselbe tun. Normalerweise bieten Toolchains Kartendateien oder andere Möglichkeiten, um zu sehen, wo sie Dinge für Sie platziert haben.
Ist es sicherlich möglich, ein Mikrocontroller-basiertes System ohne Blitz zu haben, wenn man von einer Maus oder Tastatur hört? Einige der zusammen mit vielen anderen Produkten können ohne solche Dinge funktionieren. Einige haben beispielsweise in der Logik des Chips die Möglichkeit, die USB-Aufzählung zu verwalten. Dann enthält der Betriebssystemtreiber für dieses Produkt die Firmware, lädt die Firmware herunter, um sie auf das Gerät zu rammen, startet den Prozessor und zählt den USB möglicherweise neu auf dass es jetzt eine Maus ist oder was auch immer. Nicht jeder macht es, aber es wird manchmal gemacht. Ebenso haben Sie möglicherweise ein anderes Design, bei dem eine andere Hardware oder Lösung das Programm in den Chip / RAM herunterlädt, bevor Sie den Prozessor effektiv starten, sodass das Gefühl entsteht, dass dieser Prozessor keinen Flash hat. Aber Sie haben immer noch den gleichen .text, .data ,.
quelle
Obwohl Ihre Frage mit 8051 zusammenhängt und viele richtige Antworten vorgeschlagen wurden, möchte ich auch eine Antwort für Leute aufnehmen, die hier oben landen, indem Sie nur Ihre Frage lesen und nicht die Details. Ich möchte einige Links für die ARM-Architektur der LPC-Serie von Mikrocontrollern mit Keil IDE bereitstellen. Dies sind einige wichtige Links, die mir geholfen haben, das Verhalten der Platzierung von Variablen zu verstehen:
quelle