Autor Thema: Interrupts durch Syscalls blockiert  (Gelesen 8746 mal)

OsDevNewbie

  • Beiträge: 282
    • Profil anzeigen
    • YourOS Kernel
Gespeichert
« am: 05. May 2016, 20:46 »
Hallo zusammen,

Ich wollte den Performanceunterschied zwischen den Assembler-Instructionen "int" (im Folgenden als Interrupt bezeichet) und "syscall" (im Folgenden als Syscall bezeichet) messen. Dazu habe ich eine Schleife gemacht, die 100'000'000 mal durchlaufen wird. Einmal wird dann ein Interrupt ausgelöst und das andere Mal wird ein Syscall ausgeführt. Vor und nach der Schleife speichere ich die aktuelle Laufzeit des ganzen System ab und gib am Schluss die Differenz der beiden Werte aus, also die gesamte Zeit, die die Schleife benötigt hat. Die Uptime wird vom Handler, der die Interrupts vom PIT handelt, incrementiert. Der PIT löst jede Millisekunde einen Interrupt aus. Das funktioniert soweit sehr gut. In meiner Shell kann man die aktuelle Uptime ausgeben lassen und die stimmt.

Jetzt ist das Problem, dass die Anfangszeit der Schleife immer gleich ist wie die Endzeit, was dazu führt, dass 0ms ausgegeben wird, was nicht stimmen kann, da es schon ein paar Sekunden dauert bis die Schleife beendet ist. Nach ein bisschen Debuggen ist mir aufgefallen, dass während der Schleife keine Timer-Interrupts mehr ausgelöst werden und dadurch wird auch Uptime nicht mehr incrementiert.

Nach der Schleife wird Uptime wieder incrementiert. Dieses Verhalten habe ich sowohl mit Interrupts als auch mit Syscalls.
In den Syscalls und im entsprechenden Interrupt werden Interrupts nicht deaktiviert, also sollte Uptime hoch gezählt werden, was anscheinend nicht der Fall ist.

Wieso gibt es einen "Unterbruch" der Timer-Interrupts? Sind das zu viele Interrupts für die CPU? Aber wieso passiert das dann auch bei Syscall?

Hier die entsprechende Funktion:
static void perftest()
{
size_t i, count;
SIS start, end;
uint64_t time;
printf("Starting performance test:\n");

getSysInfo(&start);
for(i = 0; i < 100000000; i++)
{
asm volatile("int $48": "=a"(count): "D"(61));
}
getSysInfo(&end);
asm volatile("int $48":: "D"(62));
time = end.Uptime - start.Uptime;
printf("Interrupts: %zu in %lums => %f/s\n", count, time, count / (double)(time?:1000.0) * 1000);

getSysInfo(&start);
for(i = 0; i < 100000000; i++)
{
asm volatile("mov %1,%%rdi;syscall": "=a"(count): "i"(61): "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11");
}
getSysInfo(&end);
asm volatile("mov %0,%%rdi;syscall":: "i"(62): "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11");
time = end.Uptime - start.Uptime;
printf("Syscalls: %zu in %lums => %f/s\n", count, time, count / (double)(time?:1000.0) * 1000);

printf("End of performance test\n");
}
getSysInfo() ist die Funktion die mir die Uptime (und noch andere Infos) zurückgibt.

Hier die entsprechenden Funktionen, die durch Interrupts oder durch Syscalls aufgerufen werden (Funktionsnummer steht im rdi-Register):

static uint64_t count = 0;
static uint64_t perfTest() //Funktion 61
{
return ++count;
}

static void resetPerfTest() //Funktion 62
{
count = 0;
}

Das ganze läuft in meiner Shell im Userspace.

Ich hoffe ihr könnt mir helfen.
Ansonsten wünsche ich euch noch einen schönen Abend.
Viele Grüsse
OsDevNewbie

Ein Computer ohne Betriebsystem ist nicht mehr wert als ein Haufen Schrott.
Ein Computer ist eine Maschine, die einem Lebewesen das kostbarste klaut, was sie selber nicht hat:
DIE ZEIT DES LEBENS

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 06. May 2016, 02:53 »
Innerhalb von Interrupthandlern sind Interrupts normalerweise gesperrt, das sollte auch für Software-Interrupts gelten. Das macht einen Kernel sehr viel einfacher. Systeme, die für verschachtelte Interrupts gebaut sind, können so konfiguriert werden, dass sie automatisch "niedriger priorisierte" Interrupts zu sperren, während ein Interrupt-Handler läuft. Der PIC kann das nicht, darum musst du verschachtelte Interrupts selbst implementieren bzw. deine Interrupt-Handler unterbrechbar machen.

Dazu muss der Handler seine eigene Interruptquelle abschalten (sonst führt ein zu schnell feuernder Interrupt zu einem Stack-Overflow) und kann dann Interrupts im Handler wieder einschalten (sti). Außerdem müssen dann alle Funktionen, die von mehreren Interrupts aus aufgerufen werden können, reentrant sein.

Zum Syscall-Befehl kann ich nichts sagen. Statt die Zeit über einen Tick-Timer zu messen, würde ich allerdings zu RDTSC o.ä. raten, das ist für sowas gemacht. Oder du lässt einen hochauflösenden Zähler frei laufen.

OsDevNewbie

  • Beiträge: 282
    • Profil anzeigen
    • YourOS Kernel
Gespeichert
« Antwort #2 am: 06. May 2016, 18:20 »
Innerhalb von Interrupthandlern sind Interrupts normalerweise gesperrt, das sollte auch für Software-Interrupts gelten.
Also bei meinem Kernel deaktivieren alle Interrupts Interrupts ausser der Interrupt, der für Syscalls verwendet wird.

Das macht einen Kernel sehr viel einfacher.
Wieso macht das den Kernel einfacher? Ich habe mir Syscalls als einen Funktionsaufruf einer Funktion im Kernel vorgestellt. Und bei einem Interrupt wird der aktuelle Status einfach wie jeder andere gesichert (ausser, dass der Stack weiterverwendet wird). Das hat bis jetzt auch gut funktioniert.
Früher hatte ich einen Bug, wobei der Zustand nicht richtig wiederhergestellt wurde, wenn ein Interrupt ausgelöst wurde während man einen Syscall ausführte, dadurch weiss ich, dass (zumindest damals) Interrupts ausgelöst werden.
Wenn ich mir das rflags-Register im Debugger (Bochs) während der Ausführung eines Syscalls anschaue, sind die Interrupts, sowohl wenn man den Syscall über die syscall-Instruction oder über Interrupts ausführt, aktiviert.

Dazu muss der Handler seine eigene Interruptquelle abschalten (sonst führt ein zu schnell feuernder Interrupt zu einem Stack-Overflow) und kann dann Interrupts im Handler wieder einschalten (sti). Außerdem müssen dann alle Funktionen, die von mehreren Interrupts aus aufgerufen werden können, reentrant sein.
Das kann nicht passieren, da eben alle Interrupts (ausser der Syscall Interrupt) Interrupts deaktivieren.

Statt die Zeit über einen Tick-Timer zu messen, würde ich allerdings zu RDTSC o.ä. raten, das ist für sowas gemacht.
RDTSC gibt einen Zähler zurück, welcher mit einer nicht weiter definierten Taktrate incrementiert wird. Diese ist aber nicht gleich der Anzahl Takte, die bisher vergangen sind, da sich der Takt an die entsprechenden Verhältnisse anpasst, aber der Takt mit der der Zähler hochgezählt wird bleibt gleich.

Trotzdem Danke für deine Antwort.
Viele Grüsse
OsDevNewbie

Ein Computer ohne Betriebsystem ist nicht mehr wert als ein Haufen Schrott.
Ein Computer ist eine Maschine, die einem Lebewesen das kostbarste klaut, was sie selber nicht hat:
DIE ZEIT DES LEBENS

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 06. May 2016, 18:43 »
Wieso macht das den Kernel einfacher? Ich habe mir Syscalls als einen Funktionsaufruf einer Funktion im Kernel vorgestellt. Und bei einem Interrupt wird der aktuelle Status einfach wie jeder andere gesichert (ausser, dass der Stack weiterverwendet wird). Das hat bis jetzt auch gut funktioniert.
Wenn du in den Syscall-Implementierungen globalen Zustand benutzt, brauchst du Locking, weil du nicht weißt, an welcher Stelle der Syscall von einem anderen Task unterbrochen werden könnte, der den globalen Zustand ebenfalls liest oder sogar ändert.

Zitat
RDTSC gibt einen Zähler zurück, welcher mit einer nicht weiter definierten Taktrate incrementiert wird. Diese ist aber nicht gleich der Anzahl Takte, die bisher vergangen sind, da sich der Takt an die entsprechenden Verhältnisse anpasst, aber der Takt mit der der Zähler hochgezählt wird bleibt gleich.
Ich dachte, du willst einfach nur Zeit messen? Dafür ist RDTSC auf den meisten CPUs ziemlich gut geeignet.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

OsDevNewbie

  • Beiträge: 282
    • Profil anzeigen
    • YourOS Kernel
Gespeichert
« Antwort #4 am: 07. May 2016, 00:33 »
Wenn du in den Syscall-Implementierungen globalen Zustand benutzt, brauchst du Locking, weil du nicht weißt, an welcher Stelle der Syscall von einem anderen Task unterbrochen werden könnte, der den globalen Zustand ebenfalls liest oder sogar ändert.
Das ist korrekt. Da muss man echt aufpassen.

Ich dachte, du willst einfach nur Zeit messen? Dafür ist RDTSC auf den meisten CPUs ziemlich gut geeignet.
OK, da hast du natürlich recht.

Aber leider löst das mein ursprüngliches Problem nicht.
Viele Grüsse
OsDevNewbie

Ein Computer ohne Betriebsystem ist nicht mehr wert als ein Haufen Schrott.
Ein Computer ist eine Maschine, die einem Lebewesen das kostbarste klaut, was sie selber nicht hat:
DIE ZEIT DES LEBENS

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 08. May 2016, 14:27 »
Also bei meinem Kernel deaktivieren alle Interrupts Interrupts ausser der Interrupt, der für Syscalls verwendet wird.
Das heißt, dass alle deine Interrupthandler mit CLI anfangen? Das sollte nicht nötig sein, wenn ich mich recht entsinne.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 08. May 2016, 18:29 »
Interrupthandler, die mit cli anfangen, sind kaputt. Aber ich nehme an, er regelt das über den Deskriptortyp (Trap Gate oder Interrupt Gate) in der IDT, und das ist völlig in Ordnung.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

OsDevNewbie

  • Beiträge: 282
    • Profil anzeigen
    • YourOS Kernel
Gespeichert
« Antwort #7 am: 08. May 2016, 19:57 »
Interrupthandler, die mit cli anfangen, sind kaputt. Aber ich nehme an, er regelt das über den Deskriptortyp (Trap Gate oder Interrupt Gate) in der IDT, und das ist völlig in Ordnung.
Das ist so korrekt.
Deshalb verstehe ich auch nicht wieso keine Interrupts ausgelöst werden während des Syscalls. Und es werden ja auch keine Interrupts ausgelöst wenn man über den syscall-Befehl die Syscalls ausführt.
Viele Grüsse
OsDevNewbie

Ein Computer ohne Betriebsystem ist nicht mehr wert als ein Haufen Schrott.
Ein Computer ist eine Maschine, die einem Lebewesen das kostbarste klaut, was sie selber nicht hat:
DIE ZEIT DES LEBENS

cacoon

  • Beiträge: 10
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 25. June 2016, 13:32 »
Ich setz mir hier mal ein Bookmark...

 

Einloggen