typedef struct process
{
/* Process Stack */
unsigned int esp;
/* Kernel Stack */
unsigned int kernel_stack;
/* Process ID */
unsigned int pid;
/* Process Name */
char *process_name;
/* Page Directory */
page_directory_t cr3;
/* Lists with Threads from this process */
list_t *threads;
/* Max. Execution Time */
unsigned int timer_ticks_for_execution;
/* Execution time left */
unsigned int timer_ticks_left;
/* Stores the Process State */
unsigned int process_state;
} process_t;
typedef struct thread
{
/* Thread Stack */
unsigned int esp;
/* Thread Name */
char *thread_name;
/* stores the thread state */
unsigned int thread_state;
/* Is Thread in Virtual 8086 Mode */
bool is_vm8086;
} thread_t;
Nicht Prozesse werden gescheduled, sondern Threads und Prozesse fassen lediglich alle Threads eines Address-Spaces zusammen.Mmmmhhhh....
Alle Threads eines Prozesses teilen sich einen Adressraum. Das ist mehr oder weniger die Definition eines Threads. cr3 wirst du natuerlich trotzdem neu laden muessen, wenn du nicht gerade zwischen Threads desselben Prozesses umschaltest.Nicht Prozesse werden gescheduled, sondern Threads und Prozesse fassen lediglich alle Threads eines Address-Spaces zusammen.Mmmmhhhh....
Ich hatte würde jedem Task einen 4 GB Addressraum geben, ausgenommen der für den Kernel reservierte Platz, was also bedeutet, dass ich bei jedem Taskwechsel das cr3 Register neuladen muss, also so:
Des Weiteren brauche ich auch eine TSS für die VM8086 Tasks. Für diese hatte ich mir dass dann so geplant, dass ich einen Deskriptor in der GDT habe, den ich jedes mal nach belieben umbiegen, sollte der aktuelle Task ein Virtueller sein.Ja, ein TSS reicht, du änderst dann einfach bei jedem Threadwechsel die ESP und SS Addressen.
Wenn der Kernel in alle Prozesse gemappt ist entfällt der zweite Schritt. Cr3 Wechsel sind sehr teuer also solltest du den Kernel in alle Prozesse mappen.
- Taskwechsel beginnt
- Kernel Page Directory laden
- Neuen Task "suchen"
- Page Directory des Tasks laden
- Task starten
Da habe ich jetzt noch eine Frage:-> Virtuellen Adressraum erstellen
Wie sieht dass dan im speziellen aus, wenn ich ein Programm starte, z.B. einen editor?
- Task wird erstellt
- Programm wird in eine freie Speicherstelle geladen
- (und nun ?)
typedef struct tss
{
unsigned short __blh, link;
unsigned int esp0;
unsigned short __ss0, ss0;
unsigned int esp1;
unsigned short __ss1, ss1;
unsigned int esp2;
unsigned short __ss2, ss2;
unsigned int cr3;
unsigned int eip;
unsigned int eflags;
unsigned int eax, ecx, edx, ebx;
unsigned int esp, ebp, esi, edi;
unsigned short __es, es;
unsigned short __ds, ds;
unsigned short __fs, fs;
unsigned short __gs, gs;
unsigned short __ldtr, ldtr;
} tss_t;
tss_t kernel_tss;
void load_kernel_tss(void)
{
gdt_set_gate(5, &kernel_tss, (&kernel_tss+sizeof(tss_t)), 0x89, 0xCF);
gdt_flush();
kernel_tss.ss0 = KERNEL_DATA_SEL;
// kernel_tss.esp0 =
LTR(5*0x8)
}
Anmerkung: LTR() ist ein Makro./* ToDo: Check value (4096) */
unsigned int *stack = kmalloc(4096)+4096;
*--stack = 0x0202; //EFLAGS
*--stack = 0x08; //CS
*--stack = (unsigned int)interrupt_handler; //EIP
//'pusha'
*--stack = 0; //EDI
*--stack = 0; //ESI
*--stack = 0; //EBP
*--stack = 0; //Just an offset, no value
*--stack = 0; //EBX
*--stack = 0; //EDX
*--stack = 0; //ECX
*--stack = 0; //EAX
// data segments pushed by IRQ handler
*--stack = 0x10; //DS
*--stack = 0x10; //ES
*--stack = 0x10; //FS
*--stack = 0x10; //GS
struct foo
{
} __attribute__((packed));
So müsste ich irgendwie standardmäßig den Portzugriff verhindern.:? Der Portzugriff ist standardmäßig in Ring3 verboten, es sei denn du setzt das IOPL oder erstellst eine I/O Permission Bitmap.
Des Weiteren habe ich noch eine Frage zur IPC. Durch das Shared Memory, könnten nur innerhalb des Tasksdie Threads untereinander kommunizieren, da der Speicher, der dafür verwendet wird anderswo vielleicht belegt ist...Nein, das wäre natürlich Blödsinn. Der Speicher innerhalb eines Prozesses ist logischerweise immer zwischen den Threads geshared, zumindest wenn man vom klassischen Model eines Prozesses als Ressourcencontainer ausgeht. Man kann das natürlich wie immer auch anders machen, ob das Sinn macht ist dann natürlich wieder eine ganz andere Frage. Unter Threads des gleichen Prozesses ist es natürlich klar, dass der ges. Speicher geshared wird.
Habe ich das so richtig verstanden?
ok.So müsste ich irgendwie standardmäßig den Portzugriff verhindern.:? Der Portzugriff ist standardmäßig in Ring3 verboten, es sei denn du setzt das IOPL oder erstellst eine I/O Permission Bitmap.
Gibt es eine möglichkeit zu prüfen, ob ein Programm nun 16-Bit oder 32-Bit Code verwendet?Das sagt dir das Dateiformat der ausführbaren Datei.
Nun das funktioniert vllt. bei *.com und anderen 16-Bit Formaten.Meinst du nicht, dass das ein von dir selbst verursachtes Problem ist? :wink: Ich mein exakt für solche Zusatzinformationen sind Dateiformate da. Wobei 16bit Code auch schon wieder so alt ist, dass es dafür kein ELF Dateiformat gibt. Ich seh auch nicht wirklich den Nutzen jetzt noch irgendwelchen 16bit Code ausführen zu wollen (einzige Ausnahme sind die VESA BIOS Extensions vllt.). Ich mein es wird demnächst sowieso Richtung EFI und logischerweise weg vom BIOS gehen...
Allerdings kann ich eine Datei Binär einmal 16-Bit und einmal 32-Bit compilieren, oder assemblieren, oder wie auch immer.
Nun das funktioniert vllt. bei *.com und anderen 16-Bit Formaten.Nur der Vollständigkeit halber: Die .com-Dateien von DOS sind einfach nur reiner Maschinencode ohne echtes Dateiformat, also flache Binaries. Womit du vielleicht eher was anfangen kannst, ist .exe. Aber nur vielleicht. ;)
Allerdings kann ich eine Datei Binär einmal 16-Bit und einmal 32-Bit compilieren, oder assemblieren, oder wie auch immer.
[BITS 32]
mov eax, ebx
mov cx, dx
produziert exact den selben binärcode wie[BITS 16]
mov ax, bx
mov ecx, edx
der unterschied besteht halt in der interpretation vom processorBinärcode | 16Bit-CPU | 32Bit-CPU |
0x89 0xd8 | mov ax, bx | mov eax, ebx |
0x66(Operand Size Prefix) 0x89 0xd8 | mov eax, ebx | mov ax,bx |
Kein Bedarf.... :-)Nun das funktioniert vllt. bei *.com und anderen 16-Bit Formaten.Nur der Vollständigkeit halber: Die .com-Dateien von DOS sind einfach nur reiner Maschinencode ohne echtes Dateiformat, also flache Binaries. Womit du vielleicht eher was anfangen kannst, ist .exe. Aber nur vielleicht. ;)
Allerdings kann ich eine Datei Binär einmal 16-Bit und einmal 32-Bit compilieren, oder assemblieren, oder wie auch immer.
0x00000000 - 0xBFFFFFFF => User Programme
0xC0000000 - 0xCFFFFFFF => Kernel
0xD0000000 - 0xDFFFFFFF => Kernel Heap
0xE0000000 - 0xFFFFFFFF => "Noch nicht vergeben"
Geändert sieht das nun So aus:0x00000000 - 0xBFFFFFFF => User Programme
0xC0000000 - 0xCFFFFFFF => Kernel
0xD0000000 - 0xD000FFFF => Kernel Heap
0xD0010000 - 0xDFFFFFFF => Multitasking (Task Stacks, Thread Stacks, ...)
0xE0000000 - 0xFFFFFFFF => Platz für IPC (Shared Memory, Message Passing, ...)
Was meint ihr dazu?Wenn du den Kernel Heap direkt hinter den Kernel klebst, dann ist für den Heap ein bisschen mehr Platz :wink: Ich würde sagen 0x10000 ist ein bisschen wenig für den Kernel Heap. Zumindest ist für die Kernel Binary überproportional viel Platz (Ich hab jetzt einfach mal das 'Kernel' in deiner Liste als das Kernel image bzw. die Binary interpretiert).
Seht ihr irgendwelche Nachteile, oder ähnliches?
Und außerdem würde ich die Task/Thread-Daten, IPC-Daten [...]Das würde ich dann eher im Einzelfall nochmal betrachten, va. bei IPC halte ich das für sehr implementationsabhängig, zB wäre es bei shared-memory Schwachsinn, das auf einem Kernel Heap zu machen und selbst bei [fixed-size] message passing man vielleicht einen spezielleren Allokator (als malloc), da man da bestimmt einiges rausholen könnte, da man ja weiß wie die Allokationsgröße ist.
Und noch eine Frage ist da. Ich bin momentan dabei Paging einzubauen.Du schreibst das OS, dir ist alles freigestellt. Du kannst höchstens fragen, was für die Programme am sinnvollsten/bequemsten ist. Wenn in LOST Speicher angefordert wird, wird (irgendein) freier physischer Speicher gesucht und an (irgend)eine freie Stelle im virtuellen Speicher des Prozesses gemappt. Die virtuelle Adresse wird dann halt an den Prozeß zurückgegeben.
Nun fordert ein Prozess per System Call Speicher an.
Nach welchen Kriterien mappt ihr den Physikalischen Speicher an welche Adresse?
Ist mir das frei gestellt, oder muss ich da irgendwas beachten?
Nun, dass irgendeine freie physikalische Page verwendet wird, ist mir klar, die verwalte ich ja in einer Bitmap und in einem Stack.Und noch eine Frage ist da. Ich bin momentan dabei Paging einzubauen.Du schreibst das OS, dir ist alles freigestellt. Du kannst höchstens fragen, was für die Programme am sinnvollsten/bequemsten ist. Wenn in LOST Speicher angefordert wird, wird (irgendein) freier physischer Speicher gesucht und an (irgend)eine freie Stelle im virtuellen Speicher des Prozesses gemappt. Die virtuelle Adresse wird dann halt an den Prozeß zurückgegeben.
Nun fordert ein Prozess per System Call Speicher an.
Nach welchen Kriterien mappt ihr den Physikalischen Speicher an welche Adresse?
Ist mir das frei gestellt, oder muss ich da irgendwas beachten?
Mir ist klar, dass bei einem VM8086-Thread die physikalische Adresse innerhalb des ersten MBs sein muss.Im VM8086 ist Paging aktiviert. Insofern muss die virtuelle Adresse unter 1MB sein, nicht (unbedingt) die physikalische. Wobei natürlich klar sein sollte, dass man das BIOS und solche Sachen mappen muss/sollte.
Wie macht ihr das mit dem flat binary Format?In flat binary würden die meisten aber auch eine Header mit eben solchen Informationen einbauen. Ansonsten gilt: Du hast es gelinkt, dann musst du auch wissen wo was in der Datei gelandet ist und wo es hingeladen werden soll. Wenn du ELF bereits hast/willst, dann ist es imho Schwachsinn Unterstützung für Flat Binary einzubauen.
Ups...Mir ist klar, dass bei einem VM8086-Thread die physikalische Adresse innerhalb des ersten MBs sein muss.Im VM8086 ist Paging aktiviert. Insofern muss die virtuelle Adresse unter 1MB sein, nicht (unbedingt) die physikalische. Wobei natürlich klar sein sollte, dass man das BIOS und solche Sachen mappen muss/sollte.
Das macht auch irgendwo sinn.ZitatWie macht ihr das mit dem flat binary Format?In flat binary würden die meisten aber auch eine Header mit eben solchen Informationen einbauen. Ansonsten gilt: Du hast es gelinkt, dann musst du auch wissen wo was in der Datei gelandet ist und wo es hingeladen werden soll. Wenn du ELF bereits hast/willst, dann ist es imho Schwachsinn Unterstützung für Flat Binary einzubauen.
Würde man solch ein Programm mit der obigen Implementation ausführen, wäre das ja ewig langsam, da wegen jeder Aktion ein extra Thread erstellt wird.Ja, in der Tat, und dafür ist es auch nicht gedacht. Der typische Anwendungsfall ist der Aufruf eines BIOS-Interrupts zum Einschalten des Grafikmodus und vielleicht noch irgendwann ein zweiter, um wieder in den Textmodus zurückzuschalten. Da tut die Performance nicht wirklich weh.
atokMeinst du "ad hoc"? ;)
steht doch da :-P :-D :oops:Zitat von: bluecodeatokMeinst du "ad hoc"? ;)