Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Low-Level-Programmierung und Experimenteller Kernel; Ansprechpartner: Dirty Oerti
Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Kerli » Fr Aug 01, 2008 6:13 pm

Dirty Oerti hat geschrieben:Danke! :)
ist schon eingebaut und funktioniert super.
Könnte das evtl auch schneller sein, als die Methode mit + und - ..?
*Optimiersüchtig^^*
Also ich glaub nicht, dass man da viel, wenn überhaupt etwas herausholen kann. Ich habs jetzt einmal kurz ausprobiert, und eigentlich keinen Unterschied gemerkt.
Dirty Oerti hat geschrieben: Der physikalische Memory Manager (Bitmap) ist damit also fertig. Ein paar kleine Änderung bei den Rückgabetypen könnte es aber evtl noch geben.

Ansonsten steht dann jetzt entweder der virtuelle Memory Manager oder der physikalische für den restlichen Speicher auf dem Programm.

Wobei das Wochenende kann ich nicht dran arbeiten...
Hört sich interessant an, aber ich werd jetzt dann nicht mehr viel ausprobieren können weil ich am Sonntag/Montag auf Urlaub fahr.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » So Aug 03, 2008 5:19 pm

Kerli hat geschrieben:Also ich glaub nicht, dass man da viel, wenn überhaupt etwas herausholen kann. Ich habs jetzt einmal kurz ausprobiert, und eigentlich keinen Unterschied gemerkt.
Naja..doch. Den Unterschied merkt man bei einer Speicheranforderung natürlich nicht.
Wird aber z.b. 16 MB Speicher angefordert, dann dürfte das jetzt schon deutlich merkbar sein.
Kerli hat geschrieben:Hört sich interessant an, aber ich werd jetzt dann nicht mehr viel ausprobieren können weil ich am Sonntag/Montag auf Urlaub fahr.
Dann wünsch ich dir nen schönen Urlaub! :)
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Mo Aug 04, 2008 10:31 am

So, ich versuche zur Zeit Paging zu initialisieren.

Das Problem: So 100%ig versteh ich noch nicht alles.
Ich halte mich im Moment stark an diesem Tutorial. Dort wird das einigermaßen gut erklärt. Allerdings verwenden die da ein anderes System, um den physikalischen Speicher zu managen.
Und das Identity Mappen bekomm ich nicht so ganz hin. Ich glaube da liegt auch das Problem, warum meins nicht funktioniert:

Denn wenn ich das, was ich bis jetzt habe, boote, dann startet der Rechner einfach neu.
Das ist ein Verhalten, das eigentlich nicht mehr vorkommen sollte.
Keine Ahnung, warum es das macht.

Naja, ich probiere weiter rum. Irgendwann raff ich's hoffentlich. :)

MfG
Daniel


*edit*

Code: Alles auswählen

void switch_page_directory(page_directory_t *dir)
{
    CurrentDir = dir;
    asm volatile("mov %0, %%cr3":: "r"(&dir->tablesPhysical));//Adresse nach CR3
    unsigned int cr0;
    asm volatile("mov %%cr0, %0": "=r"(cr0));
    cr0 |= 0x80000000; // Paging anschalten
    
    // TODO Warum kommt hier der Neustart??
    asm volatile("mov %0, %%cr0":: "r"(cr0));
}
Den Punkt, an dem der Neustart veranlasst wird habe ich jetzt immerhin schonmal gefunden.
Ich denke, es liegt daran, dass die Übergabe der Adresse nach CR3 nicht richtig funktioniert, bzw das da was falsch läuft.

*/edit*
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Di Aug 05, 2008 6:02 pm

Ok, ich bin zu dem Entschluss gekommen, dass ich das alles etwas besser organisieren muss.

Heißt:
Ich lege jetzt Speicherbereiche fest, in denen der Speicher verwaltet wird.

Ansonsten gäbe es Umwege hoch 10, um physikalischen Speicher bei aktiven Paging zu allokieren.

MfG
Daniel
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Mi Aug 06, 2008 3:03 pm

So, mal wieder ein Update:

GDT, IDT und die Bitmap werden nun im unteren Bereich des RAM verwaltet.
Sie sind also nicht mehr fest im Kernel.

Und ich bin wieder am Paging.
Bekomme es aber noch nicht wirklich zum Laufen. Ich bekomme immer einen Triple Fault, sobald ich mein Page Directory laden und das Paging Bit setzen will.

Jetzt wollte ich das ganze mal genauer Untersuchen und hab die Debug Option von bochs aktiviert.

Ja...während ich das hier schreibe öffnet Kate gerade die log Datei....30,5 MB...
Jemand ne Ahnung, wie man besser mit Bochs umgehen kann und die Ausgabe filtern kann?


*edit*
Wen es interressiert: Hier ist die debug-Ausgabe von Bochs zum Zeitpunkt, als der Fehler passiert sein muss.
Diese Version ergibt sich beim Aktivieren des Paging Bits ohne Paging Directory.

Code: Alles auswählen

00030480438d[VGA  ] 8-bit write to 03d4 = 0e
00030480456d[VGA  ] 8-bit write to 03d5 = 02
00030480471d[VGA  ] 8-bit write to 03d4 = 0f
00030480488d[VGA  ] 8-bit write to 03d5 = 1f
00030481038d[VGA  ] 8-bit write to 03d4 = 0e
00030481056d[VGA  ] 8-bit write to 03d5 = 02
00030481071d[VGA  ] 8-bit write to 03d4 = 0f
00030481088d[VGA  ] 8-bit write to 03d5 = 20
00030481638d[VGA  ] 8-bit write to 03d4 = 0e
00030481656d[VGA  ] 8-bit write to 03d5 = 02
00030481671d[VGA  ] 8-bit write to 03d4 = 0f
00030481688d[VGA  ] 8-bit write to 03d5 = 21
00030482238d[VGA  ] 8-bit write to 03d4 = 0e
00030482256d[VGA  ] 8-bit write to 03d5 = 02
00030482271d[VGA  ] 8-bit write to 03d4 = 0f
00030482288d[VGA  ] 8-bit write to 03d5 = 22
00030482838d[VGA  ] 8-bit write to 03d4 = 0e
00030482856d[VGA  ] 8-bit write to 03d5 = 02
00030482871d[VGA  ] 8-bit write to 03d4 = 0f
00030482888d[VGA  ] 8-bit write to 03d5 = 23
00030483438d[VGA  ] 8-bit write to 03d4 = 0e
00030483456d[VGA  ] 8-bit write to 03d5 = 02
00030483471d[VGA  ] 8-bit write to 03d4 = 0f
00030483488d[VGA  ] 8-bit write to 03d5 = 24
00030484038d[VGA  ] 8-bit write to 03d4 = 0e
00030484056d[VGA  ] 8-bit write to 03d5 = 02
00030484071d[VGA  ] 8-bit write to 03d4 = 0f
00030484088d[VGA  ] 8-bit write to 03d5 = 25
00030484638d[VGA  ] 8-bit write to 03d4 = 0e
00030484656d[VGA  ] 8-bit write to 03d5 = 02
00030484671d[VGA  ] 8-bit write to 03d4 = 0f
00030484688d[VGA  ] 8-bit write to 03d5 = 26
00030485238d[VGA  ] 8-bit write to 03d4 = 0e
00030485256d[VGA  ] 8-bit write to 03d5 = 02
00030485271d[VGA  ] 8-bit write to 03d4 = 0f
00030485288d[VGA  ] 8-bit write to 03d5 = 27
00030485782d[PIT  ] pit: entering timer handler
00030485782d[PIT81] clock_all:  cycles=4294
00030485782d[PIT  ] pit: RESETting timer.
00030485782d[PIT  ] deactivated timer.
00030485782d[PIT  ] activated timer.
00030485782d[PIT  ] s.last_usec=15242891
00030485782d[PIT  ] s.timer_id=1
00030485782d[PIT  ] s.timer.get_next_event_time=8638
00030485782d[PIT  ] s.last_next_event_time=34360
00030485784d[PIT  ] pit: entering timer handler
00030485784d[PIT81] clock_all:  cycles=2
00030485784d[PIT  ] pit: RESETting timer.
00030485784d[PIT  ] deactivated timer.
00030485784d[PIT  ] activated timer.
00030485784d[PIT  ] s.last_usec=15242892
00030485784d[PIT  ] s.timer_id=1
00030485784d[PIT  ] s.timer.get_next_event_time=8636
00030485784d[PIT  ] s.last_next_event_time=34358
00030485838d[VGA  ] 8-bit write to 03d4 = 0e
00030485856d[VGA  ] 8-bit write to 03d5 = 02
00030485871d[VGA  ] 8-bit write to 03d4 = 0f
00030485888d[VGA  ] 8-bit write to 03d5 = 28
00030486438d[VGA  ] 8-bit write to 03d4 = 0e
00030486456d[VGA  ] 8-bit write to 03d5 = 02
00030486471d[VGA  ] 8-bit write to 03d4 = 0f
00030486488d[VGA  ] 8-bit write to 03d5 = 29
00030488204d[VGA  ] 8-bit write to 03d4 = 0e
00030488222d[VGA  ] 8-bit write to 03d5 = 02
00030488237d[VGA  ] 8-bit write to 03d4 = 0f
00030488254d[VGA  ] 8-bit write to 03d5 = 2a
00030488396d[VGA  ] 8-bit write to 03d4 = 0e
00030488414d[VGA  ] 8-bit write to 03d5 = 02
00030488429d[VGA  ] 8-bit write to 03d4 = 0f
00030488446d[VGA  ] 8-bit write to 03d5 = 2b
00030488588d[VGA  ] 8-bit write to 03d4 = 0e
00030488606d[VGA  ] 8-bit write to 03d5 = 02
00030488621d[VGA  ] 8-bit write to 03d4 = 0f
00030488638d[VGA  ] 8-bit write to 03d5 = 2c
00030488780d[VGA  ] 8-bit write to 03d4 = 0e
00030488798d[VGA  ] 8-bit write to 03d5 = 02
00030488813d[VGA  ] 8-bit write to 03d4 = 0f
00030488830d[VGA  ] 8-bit write to 03d5 = 2d
00030488954d[VGA  ] 8-bit write to 03d4 = 0e
00030488972d[VGA  ] 8-bit write to 03d5 = 02
00030488987d[VGA  ] 8-bit write to 03d4 = 0f
00030489004d[VGA  ] 8-bit write to 03d5 = 30


/** Hier wird das Page Directory geladen, bzw sollte geladen werden **/
00030489083d[CPU0 ] MOV_CdRd:CR3 = 00000000  
00030489088d[CPU0 ] Protected Mode Activated

/** Fehler 1 **/
00030489089d[CPU0 ] PDE: entry not present
00030489089d[CPU0 ] page fault for address 0000000000101696 @ 0000000000101696
00030489089d[CPU0 ] exception(0x0e): error_code=0000
00030489089d[CPU0 ] interrupt(): vector = 14, INT = 0, EXT = 1

/** Fehler 2 **/
00030489089d[CPU0 ] PDE: entry not present
00030489089d[CPU0 ] page fault for address 00000000000000b0 @ 0000000000101696
00030489089d[CPU0 ] exception(0x0e): error_code=0000
00030489089d[CPU0 ] interrupt(): vector = 8, INT = 0, EXT = 1

/** Fehler 3 **/
00030489089d[CPU0 ] PDE: entry not present
00030489089d[CPU0 ] page fault for address 0000000000000080 @ 0000000000101696
00030489089d[CPU0 ] exception(0x0e): error_code=0000

/** Triple Fault, also sucht Bochs jetzt die Rotuine, für einen CPU Reset
00030489089d[CTRL ] searching for component 'cpu' in list 'bochs'
00030489089d[CTRL ] searching for component 'reset_on_triple_fault' in list 'cpu'
00030489089e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
*/edit*
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Do Aug 07, 2008 3:19 pm

Paging funktioniert nun ansatzweise.
Soll heißen: Der Kernel setzt den Prozessor nun in den Paging Modus.
Außerdem werden Page Faults richtig abgefangen. Allerdings wird mit denen noch nichts gemacht.

Ich bin jetzt mal wieder drann rumzugrübbeln, wie ich den physikalischen Speicher verwalte.
Das sollte ich langsam echt mal klären^^

Wobei: Ich guck mir jetzt erstmal an, ob ich Multitasking zum laufen bekomme :)
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8492
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Xin » Do Aug 07, 2008 4:41 pm

Dirty Oerti hat geschrieben:Ich bin jetzt mal wieder drann rumzugrübbeln, wie ich den physikalischen Speicher verwalte.
Das sollte ich langsam echt mal klären^^

Wobei: Ich guck mir jetzt erstmal an, ob ich Multitasking zum laufen bekomme :)
Wie startet man zwei Programme, wenn man keinen Speicher verwaltet?

Obwohl früher, als noch Betriebsystem-Goldgräber-Stimmung herschte, gab es ein beliebtes Spiel, dass im Multitasking ausgeführt wurde.

Zwei Programme überschrieben wild den Speicher und kopierten sich selbst an andere Orte im Speicher.
Wer das andere Programm zuerst 'erschossen' hat, hat gewonnen ^^
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Do Aug 07, 2008 4:52 pm

Xin hat geschrieben:Wie startet man zwei Programme, wenn man keinen Speicher verwaltet?

Code: Alles auswählen

void Programm1()
{
    while(1) {
        print("a");
    }
}

void Programm2()
{
    while(1) {
        print("b");
    }
}
Dazu brauchst du einen Taskwechsler, der als Startpunkte die Funktionen nimmt und sie solange laufen lässt, bis ein Taskwechsel ansteht (Timerinterrupt). Dann speichert er den Instruction Pointer, lädt den für die zweite Funktion, und lässt du CPU da weiter arbeiten.
Das Ergebnis bei obigen wird eine Ausgabe von "abababababab...." sein.
Und schon hat man primitivstes Multitasking.
Das kann man dann natürlich noch schön erweitern.
Xin hat geschrieben:Obwohl früher, als noch Betriebsystem-Goldgräber-Stimmung herschte, gab es ein beliebtes Spiel, dass im Multitasking ausgeführt wurde.

Zwei Programme überschrieben wild den Speicher und kopierten sich selbst an andere Orte im Speicher.
Wer das andere Programm zuerst 'erschossen' hat, hat gewonnen ^^
Das gibt es heute auch noch: "Corewars" :)
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Do Aug 07, 2008 6:43 pm

Also erstmal zum Modell, das ich mir nun überlegt habe:

Es ist, wie das jetzige, ein Bitmapmodell, nur diesmal mehrstufig.

Im alten Modell gab es eine Bitmap, die symbolisiert hat, ob eine Page/Frame von einer Größe von 4kB frei ist oder nicht.
Das ist für kleine Speichergrößen hervorragen.

Bei größeren Speichergrößen kanns aber evtl etwas länger dauern, bis man ein freies (nichtgesetztes) Bit und damit eine freie Page/Frame gefunden hat.

Deswegen unterteile ich beim neuen Modell den Speicher in gleichgroße Blöcke (a 128 MB). Ein jeder Block wird von 32 Bitmaps verwaltet. Heißt: Eine Bitmap hat nur noch 4 MB zu verwalten und ist dementsprechend kleiner und damit auch schneller zu durchsuchen.
Eine Bitmap die 4MB verwaltet braucht für sich selbst einen Speicher von 128 Byte. Nicht viel. In einen Einzigen Frame/Page passen somit 32 Bitmaps. Heißt 1 Frame/Page pro Block a 128 MB. Heißt im Endeffekt, dass 4KB in jedem 128MB großen Block zur Verwaltung gebraucht werden.

Zusätzlich dazu kommt noch die Bitmap, die die einzelnen Bitmaps verwaltet.
In einem System mit 1 GB (=1024MB) ergibt sich damit:

8 Blöcke a 128 MB
=> 8 * 4 = 32 KB Speicherbedarf für die Verwaltung
dazu kommt noch 32 Bit * 8 Blöcke = 32 Byte für die "Superbitmap"
(Ich hoffe mal, ich hab mich nicht verrechnet)

Soweit, so gut.
Was bei diesem System aber bremst:
Es muss nach jeder Allokation und nach jeder Freigabe geprüft werden, ob durch die Freigabe die Superbitmap verändert werden muss.
Außerdem dauert eine Allokation im unteren Speicherbereich länger als beim alten Model, da eine Überprüfung mehr stattfinden muss.
(Ich hoffe ich habs verständlich rübergebracht, bei Fragen bitte einfach her damit :) )

Was mir am neuen System nicht so ganz gefällt:
Was mach ich auf einem Rechner mit 64 MB?

Dazu werd ich mich nunmal mit meinen Mathekenntnissen hinsetzen müssen und ein paar Extremwertaufgaben lösen müssen. Vllcht finde ich einen Weg, schnell zu berechnen, ab wieviel MB sich das neue System lohnt und welche Aufteilung bei welcher Speichergröße am idealsten ist.

Wünscht mir Glück^^ :)

Was noch irgendwie nervt:
Ich hab nun schon extra Funktionen eingebaut, um durch GRUB die Speichergröße auf der Maschine zu erfahren.
Blöd nur, dass die Ausgabe irgendwie nicht so ganz hinhauen...
Wens interessiert:

Ausgabe von GRUB für oberen Speicher in KB, denk ich zumindest...
Speichergröße in MB

Mein Rechner:
523200
512

Bochs auf meinem Rechner:
31680
32

Bochs auf meinem Rechner:
14272
16

Erkennt jemand ein System?
Der untere Speicher beträgt meistens so 640 KB..

MfG
Daniel
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Kernelprogrammierung - Suche: Ansätze für Speicherverwaltung

Beitrag von Dirty Oerti » Mi Aug 13, 2008 8:40 pm

So. Ich muss mich fast schon entschuldigen. Mein letztes Update liegt schon etwas länger zurück.
Das liegt zum größten Teil daran, dass ich im Moment wegen eines Ferienjobs weniger Zeit für das Projekt habe.
Außerdem hat sich seit dem letzten Update nicht viel getan.

Der erste Versuch, das neue MemoryManaging Modell umzusetzen hat leider nicht geklappt.
Momentan bin ich am zweiten. Testen konnte ich es noch nicht.
Ich bin aber zuversichtlich :)

Wegen der Speichergröße:

Ich lasse die Speichergröße jetzt auf 2 hoch N aufrunden. Einfach aus dem Grund, weil ich noch keinen computer gesehen habe, der 123 MB Speicher hat.
Typsiche Werte für die Speichergröße sind ja: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 MB

Um einen eventuell doch von diesen Werten verschiedenen Speicher verwalten zu können, wird am Ende der Initialisierung der physikalischen Speicherverwaltung noch eine Überprüfung des Speichers durchgeführt werden.
Heißt im Klartext, dass (von oben beginnend) in MB Schritten abwärts versucht wird, in den Speicher zu schreiben.
Bei einem Fehler wird dieser abgefangen und die entsprechende Speicherstelle als besetzt markiert.
Das wird später zwar evtl den Code für Swapping komplizierter machen und es verbraucht auch evtl unnütze Speicher, aber ich habe keine bessere Lösung parat.

Wenn ich Zeit habe, werde ich mir demnächst auch mal den Linuxkernel ansehen und schauen, wie das dort gelöst wurde.

MfG
Daniel
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Antworten