snaums.de - Dispatchhttps://www.snaums.de/2016-05-21T12:54:00+02:00PilotOS: Processes2016-05-21T12:54:00+02:002016-05-21T12:54:00+02:00snaumstag:www.snaums.de,2016-05-21:/informatik/pilotos-processes.html<p>PilotOS [1] aims to become a simple operating system for the Raspberry Pi. I use it mainly to learn the ins and outs of operating system development, starting with simple functionality all the way to operating system architecture. In no way is PilotOS meant to be a usable operating system …</p><p>PilotOS [1] aims to become a simple operating system for the Raspberry Pi. I use it mainly to learn the ins and outs of operating system development, starting with simple functionality all the way to operating system architecture. In no way is PilotOS meant to be a usable operating system of any description, it's just for playing around.</p>
<p>Yesterday I added processes and a simple scheduler and dispatcher to the mix. A dispatcher is the functionality for switching between processes, i.e. it has to jump between the processes and save and restore the context of the process. Scheduling is more like a policy on how the next process is chosen.</p>
<p>There is cooperative and preemptive scheduling. With cooperating scheduling the process will yield the control over the CPU itself, either by calling a specific function or sometimes by calling any system call. Some operating systems did implement it in a way, that every system call is used for switching between processes. Preemptive scheduling uses a timer interrupt for the preemption of processes. I.e. at start of the system the timer will be programmed to deliver and interrupt after a certain amount of time. The interrupt will be caught by the CPU and handled by an interrupt service routine (ISR). The ISR will start the dispatching. This also means, that a process can be preempted at any point in time (see race conditions).</p>
<p>I implemented at first a cooperative switching. Well to be more precise: I implemented the framework for cooperating switching and didn't even bother testing it yet. The code is not used yet, so there is no dispatching and there are no processes yet.</p>
<p>The scheduler uses a comparison-function for choosing the next process. This function returns for two process-control-block (PCB) one, that it finds useful for any criterion it is set out to compare for. The schedule-routine is only there for applying the comparison-function to every PCB in the system. A PCB contains information about a process, in this example an ID, deadline, execution-time, last-use-information and the stack-pointer. Not all of these attributes are used usefully (yet).</p>
<p>The yielding of the CPU works with the function <em>yield</em>, which is jumping directly into the function <em>dispatch</em>. The separation of these two function is currently not useful, but maybe later on I'll find it useful that I split these two functions up. The dispatcher saves the Link-register (<em>lr</em>) onto the stack. The Link-register in ARM saves the return-address after a function call (linked branch) has occured (assembler instruction <em>bl</em>). In the called function there is the link register, which can be used with the instruction <em>mov pc, lr</em> to return to the caller-code. (PC - Program Counter, Instruction Pointer).</p>
<p>Then all registers from r1 to r12 will be saved onto the stack. r13, r14 and r15 will not be saved for they have special functionality in the ARM-core. r13 is the stack-pointer. Saving this onto the stack would be quite useless. r14 is used as the link register. One could argue, that it might be helping to save this register with all the others, but then I would have to know, whether <em>push</em> and <em>pop</em> can save all the registers in one instruction, or maybe the Assembler will split the instruction into several ones up, which only save one register at a time. Saving r15, the program counter, onto the stack is completely bogus. The program counter points into the <em>dispatch</em>-function at the moment, restoring that program-counter would lead to us not being able to leave the dispatch function ever.</p>
<p>After saving all these registers I can use them for calculations without risking loosing all of the values a process may have saved in them. But we still need to store the stack-pointer of the yielding process in our PCB, then we can load the addresses of the comparison-functions in r0 and r1 and call the <em>schedule</em>-function. Its result is stored in r0. Now I need to change the stack-pointer of the CPU, recovering the old one of the next process. Every saved register is pop'd from the stack. At last the link-register is pop'd from the stack, but it will be saved into the pc-register. So the next instruction executed will be the next one of the chosen process.</p>
<p>[1] <a href="https://github.com/naums/pilotOS">https://github.com/naums/pilotOS</a></p>
<p>Content-Image: <a href="https://openclipart.org/detail/175363/pilot-penguin">https://openclipart.org/detail/175363/pilot-penguin</a></p>PilotOS: Prozesse2016-05-21T12:14:00+02:002016-05-21T12:14:00+02:00snaumstag:www.snaums.de,2016-05-21:/informatik/pilotos-processes-de.html<p>PilotOS [1] soll ein einfaches Betriebssystem für den Raspberry Pi werden. Der Sinn hinter dem Betriebssystem ist hauptsächlich der Wissenszuwachs für mich, und die Einblicke in die Entwicklung von Betriebssystem, angefangen von einfachen Funktionalitäten, bis hin zur Architektur von Betriebssystemen. PilotOS zielt dabei in keinster Weise auf Benutzbarkeit, sondern dient …</p><p>PilotOS [1] soll ein einfaches Betriebssystem für den Raspberry Pi werden. Der Sinn hinter dem Betriebssystem ist hauptsächlich der Wissenszuwachs für mich, und die Einblicke in die Entwicklung von Betriebssystem, angefangen von einfachen Funktionalitäten, bis hin zur Architektur von Betriebssystemen. PilotOS zielt dabei in keinster Weise auf Benutzbarkeit, sondern dient einzig und allein den Spielerei-Zwecken.</p>
<p>Gestern habe ich Prozesse hinzugefügt und einen einfachen Scheduler sowie Dispatcher für Prozesse. Ein Dispatcher ist dafür da, Prozesse umzuschalten, d.h. die CPU anzuweisen in einen anderen Prozess zu springen, sowie dessen Kontext wiederherzustellen, nachdem der Kontext des verdrängten Prozess abgespeichert worden ist. Scheduling stellt die Strategie dar, nach welcher der nächste Prozess gewählt wird.</p>
<p>Es gibt sowohl kooperatives als auch preemptives Umschalten. Beim kooperativen Umschalten geben die Prozesse ihre Kontrolle über die CPU freiwillig ab, bspw. durch Rufen einer bestimmten Funktion. Manchmal ist das auch so implementiert, dass jeder Systemruf dazu führt, dass umgeschalten werden kann. Preemptives Umschalten wird durch Timer-Interrupts realisiert. D.h. beim Start des Systems wird der Hardware-Timer auf eine bestimmte Zeit programmiert, nach der er einen Interrupt auslösen soll. Dieser Interrupt wird von der CPU gefangen, und in einer bestimmten Interrupt Service Routine (ISR) verarbeitet. Diese ISR sorgt dann für das Umschalten. Das hat zur Folge, dass jeder Prozess zu jedem Zeitpunkt umgeschalten werden kann (siehe Race Conditions).</p>
<p>In PilotOS ist aktuell ein kooperatives Umschalten implementiert. Genau genommen steht erst einmal nur die Möglichkeit für kooperatives Umschalten. Der Code wird aktuell noch nirgends verwendet, und ist noch ungetestet. Der Scheduler wählt einen Prozess aus, mithilfe einer Vergleichfunktion, die anhand irgendeines beliebigen Merkmals aus zwei Prozessen (bzw. Prozess-Kontroll-Blöcken, PCB) einen zurück liefert. Die eigentliche Scheduling-Routine wendet diese Funktion also auf alle PCBs an. Ein PCB beinhaltet Informationen über die Prozesse, hier sind das Informationen wie ID, Deadline, Execution-Time, Last-Use-Informationen und der StackPointer. Nicht alle Attribute werden aktuell (sinnvoll) eingesetzt.</p>
<p>Zum Abgeben der Kontrolle der CPU gibt es die Funktion <em>yield</em>, die direkt in die Funktion <em>dispatch</em> springt. Die Trennung der beiden Funktionen ist aktuell unnötig, kann aber eventuell später nützlich sein, daher habe ich das erst einmal so implementiert. Der Dispatcher speichert sich das Link-Register (lr) auf dem Stack. Das Link-Register gibt bei ARM die Rücksprungaddresse an, nach einem Link-Branch (Assembler-Befehl <em>bl</em>), der eine Art Funktionsaufruf darstellt. In der aufgerufenen Funktion steht dann das Link-Register auf dem nächsten Befehl, in dem Code vor dem Funktionsruf, d.h. mithilfe von <em>mov pc, lr</em> lässt sich wieder zurückspringen (PC - Program Counter, Instruction Pointer).</p>
<p>Dann werden alle Register (r1,..r12) gespeichert. r13, r14, r15 werden nicht mit abgespeichert, weil diese besondere Funktionen im ARM-Kern haben. r13 ist der Stackpointer - den auf den Stack weg zu speichern wäre relativ sinnlos. r14 ist das Link-Register. Man könnt überlegen, ob es Sinn machen würde, dieses Register zusammen mit den anderen abzuspeichern - dazu müsst ich mir die Semantik von <em>push</em> und <em>pop</em> genauer zu Gemüte führen. Vor allem müsst geklärt werden, inwiefern die Befehle die Register gleichzeitig speichern und laden, oder ob der Befehl vom Assembler in mehrere kleinere Befehle zerteilt wird. Den Programm-Counter (r15) auf dem Stack zu speichern ist komplett sinnlos, weil der aktuell in der <em>dispatch</em>-Funktion steckt. Den abzuspeichern, und wieder zu laden würde verhindern, dass wir diese Funktion jemals wieder verlassen.</p>
<p>Nachdem die Register abgespeichert sind, können sie verwendet werden, ohne riskieren zu müssen, dass Werte des eigentlichen Prozesses verloren gehen. Zunächst müssen wir aber den Stackpointer des verdrängten Prozesses in den PCB schreiben. Nun lade ich die Adressen der Funktionen, die von der <em>schedule</em>-Funktion benutzt werden sollen, um den nächsten Prozess zu finden und rufe die Funktion. Das Ergebnis liegt in r0. Nun muss nur der Stackpointer umgesetzt und die zuvor gespeicherten Register wieder geladen werden. Zum Schluss hole ich dann das Link-Register noch vom Stack, allerdings speichere das Ergebnis im Program-Counter, sodass der nächste ausgeführte Befehl der nächste Befehl des Prozesses ist, der nun ausgeführt werden soll.</p>
<p>[1] <a href="https://github.com/naums/pilotOS">https://github.com/naums/pilotOS</a></p>
<p>Beitragsbild: <a href="https://openclipart.org/detail/175363/pilot-penguin">https://openclipart.org/detail/175363/pilot-penguin</a></p>