Ich habe ein Exokernel-Modell für mein ARM-Betriebssystem verwendet. Wenn eine Aufgabe von einem UART lesen möchte, ruft sie eine Bibliotheksfunktion auf, die, wenn keine Daten vorhanden sind, einen SVC-Aufruf durchführt, um die Aufgabe zu blockieren (was dazu führt, dass der Kernel die Aufgabe in die Warteschlange für diesen IRQ stellt und den IRQ aktiviert ). Wenn der Interrupt auftritt, werden alle darauf wartenden Tasks in die ausführbare Warteschlange verschoben und der Interrupt wird wieder deaktiviert.
Dieses Modell funktionierte gut, als ich eine feste Reihe von Aufgaben hatte, aber jetzt bin ich zu verknüpften Listen übergegangen, um mehr Arten von Warteschlangen zu ermöglichen (z. B. IPC-Nachrichten). Etwas in der Änderung verursacht einen Absturz. Hier ist die Debug-Ausgabe:
Creating task 0 (idle task)
task0 stack top is 2007cd20
Starting SysTick @ 100Hz
Becoming task 0
Switching to task gsm@2007c008 with SP 2007c3e8
GSM task starting
Switching to task rfid@2007c430 with SP 2007c810
Monitoring RFID reader
Blocking task rfid on IRQ 7
Switching to task gps@2007c858 with SP 2007cc38
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gsm@2007c008 with SP 2007c390
Blocking task gsm on IRQ 8
Switching to task gps@2007c858 with SP 2007cc38
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gps@2007c858 with SP 2007cc38
Starting GPS tracking
Blocking task gps on IRQ 6
Switching to task task0@2007cc80 with SP 2007ccd8
[... repeats...]
Switching to task task0@2007cc80 with SP 2007ccd8
Unblocking tasks waiting on IRQ 8
Switching to task gsm@2007c008 with SP 2007c3a0
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gsm@2007c008 with SP 2007c3a0
Fault: �� �
r0 = 2007c3a0
r1 = 10007fb8
r2 = 2007ccd8
r3 = 10007fb8
r12 = 00000008
lr = fffffffd
pc = 0070c858
psr = 00000003
BFAR = e000ed38
CFSR = 00040000
DFSR = 00000000
AFSR = 00000000
SHCSR = 00070008
Also alles gut bis zum Interrupt. Die tatsächliche Ausgabe variiert je nachdem, welcher UART zuerst Daten hat, aber das Muster ist das gleiche: Wenn ein Interrupt auftritt, tritt ein Fehler auf, wenn die nicht blockierte Aufgabe zum zweiten Mal umgeschaltet wird .
Hier sind die relevanten Code-Bits. Eine Montagescheibe:
zeptos_pendsv_isr:
push {lr}
mrs r0, psp
stmfd r0!, {r4-r11}
bl zeptos_schedule
ldmfd r0!, {r4-r11}
msr psp, r0
pop {pc}
Und die C-Funktionen:
static void pendsv(void) {
SCB->ICSR |= 1 << 28;
}
void *zeptos_schedule(void *sp) {
if (current_task) {
current_task->sp = sp;
DL_APPEND(runnable_tasks, current_task);
}
current_task = runnable_tasks;
DL_DELETE(runnable_tasks, current_task);
zeptos_printf("Switching to task %s@%p with SP %p\n", current_task->name, current_task, current_task->sp);
return current_task->sp;
}
static void block(void *sp, uint8_t irq) {
zeptos_printf("Blocking task %s on IRQ %i\n", current_task->name, irq);
current_task->sp = sp;
DL_APPEND(irq_blocked_tasks[irq], current_task);
current_task = 0;
NVIC_EnableIRQ(irq);
pendsv();
}
void __attribute__((interrupt)) zeptos_isr(void) {
int irq = (SCB->ICSR & 0xff) - 16;
zeptos_printf("Unblocking tasks waiting on IRQ %i\n", irq);
NVIC_DisableIRQ(irq);
// NVIC_ClearPendingIRQ(irq);
DL_CONCAT(runnable_tasks, irq_blocked_tasks[irq]);
irq_blocked_tasks[irq] = 0;
pendsv();
}
void __attribute__((interrupt)) zeptos_svc_isr(void) {
__disable_irq();
uint32_t *sp = (uint32_t *) __get_PSP();
uint32_t pc = sp[6];
uint8_t svc_type = *((uint8_t *) pc - 2);
switch (svc_type) {
case 0:
sleep(sp[0]);
break;
case 1:
block(sp, sp[0]);
break;
default:
zeptos_puts("Bad SVC type\n");
}
__enable_irq();
}
void Zeptos_BlockOnIrq(uint8_t irq) {
asm("svc 1");
}
SVC, SysTick und PendSV haben die Priorität 29, 30 bzw. 31.
Irgendwelche Vorschläge? Wo soll ich suchen?
Ich habe das Problem endlich gefunden . Wenn mein SVC-Handler aufruft block
, um eine Aufgabe auf die Sperrliste zu setzen, enthält der Stack dieser Aufgabe nur die von der Hardware gestapelten Register und nicht die, {r4-r11}
die der Scheduler erwartet, wenn er sie später erneut ausführt.
Die schnelle Lösung besteht darin, einen Assembly-Shim für den SVC-ISR zu haben, der die zusätzlichen Register stapelt und entstapelt, und die C- zeptos_svc_isr
Funktion ebenso einen Stapelzeiger zurückgeben zu lassen zeptos_schedule
. Es funktioniert, aber einige Refactoring ist jetzt in Ordnung.
Hier gibt es einiges. Sie haben das Speichern von r12 im Store verpasst.
APCS schreibt auch nicht vor, r0-r3 zu speichern, Sie müssen auch etwas tun, um diese zu speichern.
IIRC, die Ausnahmen haben immer noch getrennte r13 und r14, daher müssen Sie währenddessen möglicherweise auch den normalen Modus r13/r14 speichern/wiederherstellen.
Tangr
DL_X
Funktionen/Makros atomar sind? Ich vermute, dass Ihre Linked-List-Implementierung nicht threadsicher ist. Bearbeiten: Wie sieht Ihr Kontextwechselcode aus?Isvara
DL_*
Funktionen sind nicht atomar. Sie sind von uclist. Ich habe jedoch versucht, Interrupts um die Listenänderungen herum zu deaktivieren/aktivieren, was keinen Unterschied machte. Der Kontextumschaltcode istzeptos_pendsv_isr
undzeptos_schedule
. Im Moment ist es ziemlich einfaches Round-Robin.W5VO