Ich weiß, dass einige Leute sagen werden, dass ich kein Betriebssystem schreiben muss, weil es viele Möglichkeiten gibt. Aber ich bin nicht daran interessiert, ein Betriebssystem zu verwenden, um ein bestimmtes Problem zu lösen. Ich möchte selbst einen schreiben, um zu lernen, wie man es macht.
Ich weiß viel über die Theorie von Betriebssystemen und ich weiß auch, wie man Mikrocontroller in Bare Metal programmiert. Aber ich weiß sehr wenig über die Konfigurationsdetails, die nicht direkt mit der Programmierung zusammenhängen. Ich spreche von Link-Befehlsdateien, Startcode usw.
Meine Frage ist: Was muss ich über diese "Konfigurations"-bezogenen Dinge wissen, um das Betriebssystem zum Laufen zu bringen? Bitte verlinken Sie, wenn möglich, einige externe Inhalte zu diesen Dingen.
Hier sind einige Dinge, die einem sofort in den Sinn kommen.
Und das ist nur eine sehr kurze Liste, die mir aus den Fingern fließt, ohne darüber nachzudenken. Ich bin mir sicher, dass ich diese Liste verdoppeln könnte, wenn ich weitere 5 Minuten aufwenden würde. (Zum Beispiel habe ich nicht einmal etwas darüber angesprochen, was ein Debugger in Bezug auf Informationen und / oder geänderten oder eingefügten Code zur Unterstützung seiner Operationen benötigen könnte. Ich habe auch nicht die Unterschiede erörtert, die zwischen Harvard- und von Neumann-Speichersystemen bestehen. Noch Ich habe das standardmäßige "Programmmodell" der Organisation besprochen [Code; Konstanten; Init-Daten; Uninit-Daten; Heap; Stack usw.])
Ich würde empfehlen, dies in langsamen Schritten anzugehen. Da Sie behaupten, sich bereits mit Bare-Metal-Programmierung auszukennen und sich auch mit Betriebssystemen auszukennen, möchte ich Ihnen einfach empfehlen, das allererste XINU-Buch von Douglas Comer zu lesen (es ist um 1984, hat ein rotes Cover und es gibt keinen Band 2 , usw.) Dann sehen Sie, ob Sie eine kooperative Vermittlung zusammenschustern könnenBetriebssystem. Das bedeutet KEINE VORBEUGUNG. Es bedeutet nur THREADS – keine vollständigen, separaten Prozesse – sondern Threads, die denselben Coderaum, dieselben Konstanten, statischen Daten und denselben Heap teilen; mit dem einzigen Unterschied, dass sie separate Stacks haben. Unterstützen Sie einen switch()-Aufruf, damit dieser kooperative Thread-Wechsel funktioniert. Es muss auch Hardware-Treiberereignisse unterstützen, ohne dabei versehentlich den Stack eines Threads zum Überlaufen zu bringen. Sie müssen sorgfältig entwerfen, wie Sie mit Hardwareereignissen und ihrem Treibercode umgehen. (Ich habe dies in Low-Level-Hardware-Antwortcode + High-Level-Thread-zugänglichen Code aufgeteilt, die durch Puffer getrennt sind, um die beiden voneinander zu entkoppeln, damit sie unabhängig voneinander arbeiten können und gleichzeitig die Hardware vollständig unterstützen.)
Wenn Sie den nächsten Schritt machen möchten, fügen Sie Nachrichten zwischen Threads hinzu. Verwenden Sie ein einfaches Wort – nur ein Wort – für jeden Thread und lassen Sie es von einem anderen Thread schreiben. Überschreiben Sie es tatsächlich. Machen Sie das nicht kompliziert. Sehen Sie, ob Sie einzelne Wortmeldungen zwischen den Prozessen erhalten können.
Fügen Sie dann eine Schlafwarteschlange hinzu und stellen Sie einen Timer bereit, der Threads aus der Schlafwarteschlange zurück in die Ausführungswarteschlange verschieben kann.
Fügen Sie dann Semaphor-Warteschlangen hinzu.
Wenn Sie so weit kommen, haben Sie viel gelernt.
Übrigens habe ich weniger als zwei Arbeitstage (Montag bis zum frühen Dienstagnachmittag) gebraucht, um alle oben genannten Funktionen zum Laufen zu bringen – von Grund auf neu und ohne eine einzige Codezeile aus früheren Projekten, also einfach so schnell tippen wie ich denken könnte - einschließlich einer vollständigen Mischung aus Assembler und C, um Hardwareereignisse und so weiter zu verarbeiten. Ich hatte Run/Sleep/Semaphor-Warteschlangen, Timer und kooperative Threads zusammen mit Ausnahmebehandlung pro Thread hinzugefügt ... in weniger als zwei Tagen.
Dies ist also KEINE besonders schwierige Aufgabe, die vor uns liegt.
Haben Sie es.
Die meisten Mikrocontroller sind dafür nicht wirklich geeignet. Sie wollen mehr von einem Allzweck-Mikroprozessor.
Mit einigen High-End-32-Bit-Mikros wie ARM und PIC32 können Sie möglicherweise ein vernünftiges Betriebssystem schreiben. Für ein allgemeines Betriebssystem benötigen Sie einen Prozessor, der Benutzercode so ausführen kann, dass der Benutzercode dem System keinen Schaden zufügen kann. Dies wird normalerweise durch einen privilegierten Modus und einen Benutzermodus erreicht. Die meisten Micros haben das nicht.
Ein weiteres Problem besteht darin, dass die meisten Mikros nur aus dem ROM heraus ausgeführt werden, nicht aus dem RAM. Das macht das Laden von beliebigem Benutzercode und das anschließende Ausführen schwierig oder unmöglich. Der gesamte RAM- und ROM-Speicherplatz ist normalerweise ebenfalls festgelegt und kann und wird nicht extern erweitert. Das verhindert kein Betriebssystem, macht aber die Anwendungen, die das System ausführen kann, ziemlich begrenzt. Die meisten Mikros haben keine MMUs, die reale auf logische Speicheradressen umwandeln können und beim Versuch, auf bestimmten Speicher zuzugreifen, Traps verursachen. Das macht das Paging schwierig oder unmöglich.
Schau dir mal den PIC32 an. Es kann vom RAM ausgeführt werden und verfügt mindestens über eine grundlegende MMU. Einige Varianten haben genug Speicher, um nützliche Programme im Benutzermodus zu ermöglichen.
Was die Details der Verknüpfung betrifft, müssen Sie wirklich die Handbücher für die Tools lesen. Es gibt keinen Ersatz dafür, zu verstehen, was der Linker tut und wie man ihn steuert. Sie müssen RTFM. Es gibt keine Abkürzung.
Ich vermute, dass es möglich sein könnte, ein Echtzeitbetriebssystem ohne spezielle Kenntnisse oder Anpassungen des Linker-Direktivenskripts und des C-Laufzeit-Startcodes zu implementieren.
Das Linkerdirektivenskript identifiziert Regionen und Abschnitte des Speichers. Wenn der Toolanbieter eine standardmäßige Linker-Skriptdatei bereitstellt, die für Ihre Bare-Metal-Anwendungen funktioniert, könnte sie auch für Ihre RTOS-basierte Anwendung funktionieren. Sie würden das Linkerskript anpassen, wenn Ihre Anwendung spezielle Speicheranforderungen hat. Beispiele umfassen eine Anwendung für ein kundenspezifisches Board mit externen Speichern, eine Anwendung mit spezialisierten Speicherabschnitten, die eine spezialisierte Initialisierung erfordern, und eine Anwendung, die in Verbindung mit einem Bootloader-Programm arbeitet. Ich kann mir keinen Grund vorstellen, warum die Verwendung eines RTOS Sie dazu zwingen würde , das Linker-Skript anzupassen. Das Wissen darüber, wie das Linker-Skript angepasst werden kann, ist wichtig für jede spezialisierte Anwendung, die dies erfordert, unabhängig davon, ob ein RTOS verwendet wird.
Der Startcode initialisiert die C-Laufzeitumgebung für Ihre Anwendung. Wenn der Standardstartcode des Toolanbieters für Ihre Bare-Metal-Anwendung gut genug ist, dann ist er möglicherweise auch für Ihre RTOS-basierte Anwendung gut genug. Der Startcode kopiert Initialisierungswerte vom ROM in den RAM und löscht nicht initialisierte Daten. (Für C++ ruft es die Konstruktoren für statisch zugewiesene Objekte auf.) Sie würden den Startcode anpassen, wenn Sie spezielle Speicherabschnitte haben, die eine spezielle Initialisierung erfordern. Sie können auch den Startcode anpassen, wenn Ihr Board über eine spezielle Hardwareinitialisierung verfügt, die vor der Initialisierung der Laufzeitumgebung (oder vor main()
) durchgeführt werden sollte. Ich kann mir keinen Grund vorstellen, warum die Verwendung eines RTOS erzwingen würdeSie können den Startcode anpassen. Das RTOS kann von initialisiert werden main()
(nachdem der Startcode vollständig ist). Das Wissen darüber, wie der Startcode angepasst werden kann, ist wichtig für jede spezialisierte Anwendung, die dies erfordert, unabhängig davon, ob ein RTOS verwendet wird.
Sie werden jedoch alles über die CPU-Register wissen wollen und wie Interrupts von der CPU gehandhabt werden. Sie müssen wissen, welche CPU-Register gespeichert/wiederhergestellt werden sollen, um den Kontext zu wechseln. Und Interrupts sind eine erstklassige Gelegenheit, wenn ein RTOS-Kontextwechsel auftritt.
Sehen Sie sich neben der FreeRTOS-Dokumentation auch die uC/OS-III-Bücher von Micrium an.
Toni M
Benutzer49894
Daniel
Benutzer49894
user_1818839
Benutzer253751
Benutzer253751
Benutzer253751