Moin,
wir haben in einem Uni-Projekt einen SPARC-Prozessor so veraendert, dass
er in einem interleaved Betrieb mehrere Threads gleichzeitig ausfuehren
kann. Dem Programm erscheint der Prozessor dabei wie sechs parallele
Rechner. Bei einem Reset beginnt der Prozessor jeden Thread an einer
bestimmten Stelle im Bytecode. Das funktioniert mit einfachen Programmen
bestens, aber wie mache ich es mit komplizierteren, in die Bibliotheken
gelinkt werden? Gibt es eine Moeglichkeit, die einzelnen Threads soweit
zu linken, dass die Bibliotheken schon eingebunden sind, und dann das
ganze in ein Programm mit den entsprechenden Einsprungstellen zu linken?
Muss ich dabei fuer jedes Programm einen eigenen Linkerscript schreiben
oder geht es mit einem zentralen? Speziell, wie behandle ich die
einzelnen init-Sektionen und wie unterscheide ich die?
Gruss, Joern
Dein Grundproblem ist, soweit ich verstanden habe, die Trennung der
Adressräume der einzelnen Programme voneinander.
In üblichen Multitasking-Umgebungen hilft es enorm, wenn die CPU eine
MMU hat und jedes Programm so seinen eigenen virtuellen Adressraum
erhält, wenn der Scheduler des Betriebssystem dem Programm seinen
Rechenzeitabschnitt zuteilt.
Was du mit "interleaved Betrieb" meinst, ist mir unklar. Habit ihr den
üblichen Scheduler durch Veränderung der CPU per Hardware implementiert?
Die normale SPARC-Implementierung besitzt eine siebenstufige Pipeline.
Wir haben diese so veraendert, dass sie mit jedem Takt den Thread
wechselt, so dass nur unabhaengige Threads gleichzeitig in der Pipeline
sind, was einige Hazards zu vermeiden hilft und speziell bei uns die
praktisch gleichzeitige Ausfuehrung mehrerer Interrupthandler
ermoeglichen soll. Eine MMU besitzt der SPARC auch, die wollten wir aber
ungern veraendern, weil dafuer die Zeit wohl nicht reicht. Statt dessen
wuerden wir gern die einzelnen Threads so linken, dass sie ein Programm
in einem zusammenhaengenden Adressraum ergeben. Das geht mit einfachen
Programmen bestens, aber initialisierungs Code im .init-Segment stellen
mich da vor ein Problem. Im Moment ist das Multithreading so geloest,
dass der Prozessor nach einem Reset sieben verschiedene Einsprungstellen
nacheinander laed. Kann ich nun die einzelnen .init-Segmente so linken,
dass er fuer jeden Thread zu dessen .init-Code springt?
Gruss, Joern
Ich sehe da zwei Wege, verstehe aber den Benefit eurer Modifizierung
nicht.
1/ Anlehnend an deine Idee wird jedes Programm für sich inkl. dem
benötigten Startupcode und den Libraries gelinkt. Allerdings nicht wie
üblich alle ab der gleichen Startadresse. Sondern jedes Programm bekommt
seinen eigenen Adressraum. Eingestellt wird die Lage des Adressraums
über die Linkercontrolskripte. Die fertigen Binärfiles werden dann
zusammengefügt. Es kann mühsam sein, die Grösse der einzelnen Sektionen
im Voraus abzuschätzen.
2/ Wenn man eine thread-safe C-Library und ggf. auch einen solchen
Startupcode hat, dann kann man den mehrfachen Librarycode und ggf.
Startupcode einsparen, indem man diesen Code einmal in eine eigene
Sektion legt. Die Restprogramme werden wie bei 1/ erzeugt und in jeweils
eigene Sektionen gelegt.
Bei beiden Methoden kann man Thread/Programm-gemeinsame Daten haben, die
dann ebenfalls in eine eigene, gemeinsame RAM-Sektion kommen. Der
Nachteil bei beiden Methoden ist die durch das Linken an eine absolute
Adresse die feste Zuordnung der Programme zu einem Speicherbereich.
Gerade das wäre der Benefit einer MMU - alle Programme würden an eine
gleiche Startadresse gelinkt und dann durch die MMU auf unterschiedliche
physikalische Adressen abgebildet.
Was ich nicht verstehe ist, welchen Nutzen das Ummodeln der Pipeline in
eurem Sinn hat (ausser dem Lerneffekt und dem Forschungsdrang und dem
Spass). Abgesehen von einer Performanceeinbusse durch den Verzicht auf
die Pipeline und Möglichkeiten zur Codeoptimierung für eine CPU mit
Pipeline sehe ich da viele Herausforderungen gegenüber einem
konventionellen Kernel mit Scheduler, der sich gleichzeitug auch ale
Einziger um die Hardwareresourcen (Interrupts, Periferie wie z.B. UART,
...) kümmern kann. Wenn eure Threads/Programme solche Resourcen wollen,
wird es IMHO mit eurem Verfahren ohne Kernel schwierig.
Danke erstmal, das hoert sich doch schon ziemlich hilfreich an. Kann man
den Linker irgendwie mit dem Verbinden der einzelnen Programme betrauen?
Schließlich moechte ich die Einsprungstellen alle am Anfang haben, da
eine Assemblerdatei einlinken und von der aus zu den jeweiligen
Prozeduren springen. Das ginge zwar auch noch irgendwie so, waere aber
mit dem Linker wohl weniger umstaendlich.
Das Projekt ist natuerlich ziemlich akademisch und auch nicht so der
richtige Spass. Der einzige Vorteil dieser Interleaved Pipeline ist die
Moeglichkeit, die Worst Case Execution Time einschaetzen zu koennen und
gleichzeitig beliebig unvorhersehbare (auch statistisch unvorhersehbare)
asynchrone Interrupts behandeln zu koennen. Geopfert wird dabei neben
der zusaetzlichen Chipflaeche natuerlich jede Menge Leistung.
Ich werde dann doch mal einen Blick in die MMU werfen, auch wenn eine
Anpassung dort wohl den Umfang des Projektes sprengen duerfte.
Gruss, Joern
Oh, für das Verbinden den einzelnen Programme halte ich den Linker für
zu mächtig.
Der Linker arbeitet auf Objektcode-Ebene, d.h. alle Symbole sind noch
sichtbar. In den einzelnen Programmen sind bei Alternative 1 alle
Symbole kritisch, die gleich sind; bei Alternative 2 einige die Probleme
machen können (z.B. main) und einige, die harmlos sind (z.B. die aus der
thread-safe Library). Das wird ein Riesenkuddelmuddel wg. mehrfachen
Definitionen oder man muss alle kritischen Symbole unterscheidbar machen
;-(
So ein Tool, das wie Objcopy arbeitet, wäre wohl besser geeignet.
http://www.gnu.org/software/binutils/manual/html_chapter/binutils_3.html
Die Arbeitsweise wäre, aus allen gelinkten Programmen und dem
übersetzten Assemblerfile zunächst Binaries zu machen. Dann diese per
Shell copy binär zu einem Megabinary aneinander hängen. Das Eintragen
der Einsprungadressen in das Megabinary kann man banal mit einem
Binärpatch im Endprodukt oder im übersetzten Assemblerfile machen oder
gleich im Assemblerfile hart als Konstante codieren. Anschliessend das
Megabinary ggf. in ein Hexfile o.ä. umwandeln, so wie es das
Programmertool braucht.
Du ahnst sicher den Haken dahinter. Man braucht von Anfang an eine Idee
wo die Startadressen zu liegen kommen und wie gross die Speicherslots
für die Programme sind, um das Linkerskript zu machen... Entweder brutal
indem man den realen Spricher durch die Zahl der Programme teilt oder
indem man eine Versuchsserie fährt und den Platz dann nach Bedarf
zuteilt.