Betriebssystembau (BSB) 3. Übung http://ess.cs.tu-dortmund.de/de/teaching/ws2012/bsb/ Olaf Spinczyk olaf.spinczyk@tu-dortmund.de http://ess.cs.tu-dortmund.de/~os AG Eingebettete Systemsoftware Informatik 12, TU Dortmund
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 2
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 3
Interrupts und Traps Interrupts durch Hardware ausgelöste Ereignisse asynchron zum Programmablauf nicht vorhersagbar unterbrochener Programmablauf wird nach Bearbeitung wieder aufgenommen Traps durch Instruktion (direkt oder indirekt) ausgelöst synchron zum Programmablauf unterbrochener Programmablauf kann wieder aufgenommen oder auch abgebrochen werden 30.10.12 Betriebssystembau: 3. Übung 4
Traps Überblick Traps werden von der CPU ausgelöst, wenn während der Ausführung der aktuellen Instruktion ein Problem festgestellt wird... Division durch Null Busfehler d.h., ein Programm generiert ungültige Adresse Betriebssystem-Eingriff erforderlich (z.b. Seitenfehler) ungültige Instruktion...oder aber auch, wenn die ausgeführte Instruktion direkt einen Trap hervorrufen soll int 0x80 syscall / sysenter 30.10.12 Betriebssystembau: 3. Übung 5
(nochmal) i386 IDT: Aufbau 0 31 255 IDT Traps Hardware/Software IRQs Einträge 0-31 für Traps (fest) Number Description 0 Divide-by-zero Trap = Ausnahme, die synchron 1 Debug exception 2 Non-Maskable Interrupt (NMI) zum Kontrollfluss auftritt 3 Breakpoint (INT 3) 4 Overflow Division durch 0 5 Bound exception Seitenfehler 6 Invalid Opcode 7 FPU not available Unterbrechungspunkt 8 Double Fault 9 Coprocessor Segment Overrun... 10 Invalid TSS 11 Segment not present 12 Stack exception Einträge 32-255 für 13 General Protection IRQs (variabel) 14 Page fault 15 Reserved Software (INT <nummer>) 16 Floating-point error 17 Alignment Check Hardware (INT-Pin der CPU 18 Machine Check auf HIGH, Nummer auf 30.10.12 19-31 Reserved By Intel Betriebssystembau: 2. Übung 6 Datenbus)
Traps Beispiele Division durch Null 1 int main() { 2 int i, j; 3 4 i=3; 5 j=0; 6 7 return i/j; 8 } $ gdb./divnull (gdb) run Program received signal SIGFPE, Arithmetic exception. 0x0804836b in main () at divnull.c:7 7 return i/j; main: leal andl pushl pushl movl pushl subl movl movl movl movl sarl idivl addl popl popl leal ret 4(%esp), %ecx $-16, %esp -4(%ecx) %ebp %esp, %ebp %ecx $16, %esp $3, -12(%ebp) $0, -8(%ebp) -12(%ebp), %edx %edx, %eax $31, %edx -8(%ebp) $16, %esp %ecx %ebp -4(%ecx), %esp 30.10.12 Betriebssystembau: 3. Übung 7
Traps Beispiele Schutzverletzung 1 int main() { 2 int *p; 3 4 p = (int *)0; 5 6 return *p; 7 } $ gdb./segfault (gdb) run Program received signal SIGSEGV, Segmentation fault. 0x0804835f in main () at segfault.c:6 6 return *p; main: leal andl pushl pushl movl pushl subl movl movl movl addl popl popl leal ret 4(%esp), %ecx $-16, %esp -4(%ecx) %ebp %esp, %ebp %ecx $16, %esp $0, -8(%ebp) -8(%ebp), %eax (%eax), %eax $16, %esp %ecx %ebp -4(%ecx), %esp 30.10.12 Betriebssystembau: 3. Übung 8
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 9
Race Conditions Wikipedia: A race condition [...] is a flaw in a system or process whereby the output of the process is unexpectedly and critically dependent on the sequence or timing of other events. [http://en.wikipedia.org/wiki/race_condition] Ursprung des Begriffs im Elektronikentwurf 30.10.12 Betriebssystembau: 3. Übung 10
Race Conditions in Software Parallel ablaufende Vorgänge (Prozesse, Threads) verwenden gemeinsamen Status Beispiel: zwei gleichzeitig ablaufende Prozesse wollen den (ASCII-)Wert eines Zeichens im Bildschirmspeicher erhöhen. Die Prozesse sehen beide wie folgt aus: 1 int main(void) { 2 volatile unsigned char *p; 3 4 p = (unsigned char *)0xb8000; 5 6 (*p)++; 7 return 0; 8 }??? 30.10.12 Betriebssystembau: 3. Übung 11
Race Conditions in Software Parallel ablaufende Vorgänge (Prozesse, Threads) verwenden gemeinsamen Status Beispiel: zwei gleichzeitig ablaufende Prozesse wollen den (ASCII-)Wert eines Zeichens im Bildschirmspeicher erhöhen. Die Prozesse sehen beide wie folgt aus: 1 int main(void) { 2 volatile unsigned char *p; 3 4 p = (unsigned char *)0xb8000; 5 6 (*p)++;!!! 7 return 0; 8 } 30.10.12 Betriebssystembau: 3. Übung 12
Race Conditions in Software Die harmlos aussehende Instruktion *p++ ist nicht atomar! In Assembler sieht das wie folgt aus: main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $16, %esp movl $753664, -8(%ebp) movl -8(%ebp), %eax 4 p = (...)0xb8000; movzbl (%eax), %eax leal 1(%eax), %edx 6 *p++; movl -8(%ebp), %eax movb %dl, (%eax) [...] popl %ecx popl %ebp leal -4(%ecx), %esp ret 30.10.12 Betriebssystembau: 3. Übung 13 Interrupt und Kontextwechsel
Race Conditions in Software movl $753664, -8(%ebp) movl -8(%ebp), %eax movzbl (%eax), %eax leal 1(%eax), %edx movl movb Interrupt, führt zu Kontextwechsel -8(%ebp), %eax %dl, (%eax) 'A' = 0x41 'B' = 0x42 'B' = 0x42 movl $753664, -8(%ebp) movl -8(%ebp), %eax 'A' = 0x41 movzbl (%eax), %eax leal 1(%eax), %edx 'B' = 0x42 movl -8(%ebp), %eax movb %dl, (%eax) Das Resultat ist falsch (erwartet: 'C' = 0x43), weil der Compiler das Zeichen im Bildschirmspeicher in einem Register zwischengespeichert hat. Jeder Prozess ist dann von diesem Wert als aktuell ausgegangen. Wäre der Interrupt zwei Instruktionen vorher oder später aufgetreten, wäre das Ergebnis korrekt! Race Conditions sind schwer zu finden und zu debuggen! 30.10.12 Betriebssystembau: 3. Übung 14
Konsistenzprobleme bei Interrupts Beispiel 1: Systemzeit hier schlummert möglicherweise ein Fehler... das Lesen von global_time erfolgt nicht notwendigerweise atomar! Beispiele aus der letzten Vorlesung 32-Bit-CPU: mov global_time, %eax 16-Bit-CPU (little endian): mov global_time, %r0; lo lo mov global_time+2, %r1; hi hi kritisch ist eine Unterbrechung zwischen den beiden Leseinstruktionen bei der 16-Bit-CPU Instruktion global_time Resultat hi hi // lo lo r1 r1 // r0 r0? 002A FFFF?? mov global_time, %r0 002A FFFF? FFFF /* /* Inkrementierung */ */ 002B 0000? FFFF mov global_time+2, %r1 002B 0000 002B FFFF Beispiel 2: Ringpuffer auch die Pufferimplementierung ist kritisch... Ausführung Zustand 'a' 'a'?? 1 1 0 char consume() { buf buf [0] [0] [1] [1] [2] [2] occ. occ. nextin nextin nextout int int elements = occupied; // // 1 if if (elements == == 0) 0) return 0; 0; char result = buf[nextout]; //'a' 'a' 'a'?? 1 1 0 nextout++; nextout %= %= SIZE; buf buf [0] [0] [1] [1] [2] [2] occ. occ. nextin nextin nextout void produce(char data) { // // 'b' 'b' int int elements = occupied; // // 1! 1! if if (elements == == SIZE) return; buf[nextin] = data; 'a' 'a' 'b' 'b'? 2 2 0 nextin++; nextin %= %= SIZE; occupied = elements + 1; 1; // // 2 buf buf [0] [0] [1] [1] [2] [2] occ. occ. nextin nextin nextout } occupied = elements 1; 1; // // 0 'a' 'a' 'b' 'b'? 0 2 0 return result; // // 'a' 'a' buf buf [0] [0] [1] [1] [2] [2] occ. occ. nextin nextin nextout } 30.10.12 Betriebssystembau: 3. Übung 15
Ursache Anwendungskontrollfluss (A) Kontrollflüsse...... von oben... main() Wir müssen (irgendwie) die Konsistenz sicherstellen. BS-Kern consume() buf[...] produce()... begegnen sich im Kern. handler()...und von unten... Unterbrechungsbehandlung (UB) 30.10.12 Betriebssystembau: 3. Übung 16
Naiver Lösungsansatz Zweiseitige Synchronisation Zweiseitige Synchronisation funktioniert natürlich nicht! gegenseitiger Ausschluss durch Schlossvariable (Mutex, Spin-Lock,...) wie zwischen zwei Prozessen Anwendungskontrollfluss (A) main() BS-Kern consume() handler() buf[...] produce() Unterbrechungsbehandlung (UB) char consume() { mutex.lock();... char result = buf[nextout++];... mutex.unlock(); return result; } void produce(char data) { mutex.lock();... buf[nextin++] = data;... mutex.unlock(); } 30.10.12 Betriebssystembau: 3. Übung 17
Besserer Lösungsansatz Einseitige Synchronisation Mit einseitiger Synchronisation funktioniert es... [warum eigentlich?] Unterdrückung der Unterbrechungsbehandlung im Verbraucher Operationen disable_interrupts() / enable_interrupts() (im Folgenden o.b.d.a. in Intel -Schreibweise: cli() / sti()) Anwendungskontrollfluss (A) main() BS-Kern consume() handler() buf[...] produce() Unterbrechungsbehandlung (UB) char consume() { cli();... char result = buf[nextout++];... sti(); return result; } void produce(char data) { // hier nichts zu tun... buf[nextin++] = data;... // hier nichts zu tun } 30.10.12 Betriebssystembau: 3. Übung 18
Erstes Fazit Konsistenzsicherung zwischen Anwendungskontrollfluss (A) und Unterbrechungsbehandlung (UB) muss anders erfolgen als zwischen Prozessen. Die Beziehung zwischen A und UB ist asymmetrisch Es handelt sich um verschiedene Arten von Kontrollflüssen UB unterbricht Anwendungskontrollfluss implizit, an beliebiger Stelle hat immer Priorität, läuft durch (run-to-completion) A kann UB unterdrücken (besser: verzögern) explizit, mit cli / sti (Grundannahme 5 aus der letzten Vorlesung) Synchronisation / Konsistenzsicherung erfolgt einseitig Diese Tatsachen müssen wir beachten! 30.10.12 (Das heißt aber Betriebssystembau: auch: Wir können sie 3. Übung ausnutzen) 19
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 20
i386 Interrupt-Deskriptortabelle (IDT) maximal 256 Einträge Basisadresse und Größe in IDTR 8 Byte pro Eintrag (Gate) Task-Gate (Hardwaretasks) Trap-Gate (Ausnahmehandler) Interrupt-Gate (Ausnahmehandler + cli) IDTR 30.10.12 Betriebssystembau: 3. Übung 21
i386 Interrupt-Deskriptortabelle (IDT) maximal 256 Einträge [SECTION.data] ; 'interrupt Basisadresse descriptor und Größe table' in IDTR mit 256 Eintraegen. 8 Byte pro Eintrag (Gate) idt: %macro idt_entry Task-Gate (Hardwaretasks) 1 Trap-Gate dw (wrapper_%1 (Ausnahmehandler) - wrapper_0) & 0xffff dw 0x0008 Interrupt-Gate dw 0x8e00 (Ausnahmehandler + cli) dw ((wrapper_%1 - wrapper_0) & 0xffff0000) >> 16 %endmacro ;... wird automatisch erzeugt. %assign i 0 %rep 256 idt_entry i %assign i i+1 %endrep IDTR idt_descr: dw 256*8-1 ; idt enthaelt 256 Eintraege 30.10.12 dd idt Betriebssystembau: 3. Übung 22
i386 Interrupt-Deskriptortabelle (IDT) maximal 256 Einträge [SECTION.data] ; 'interrupt Basisadresse descriptor und Größe table' in IDTR mit 256 Eintraegen. 8 Byte pro Eintrag (Gate) idt: %macro idt_entry Task-Gate (Hardwaretasks) 1 Trap-Gate dw (wrapper_%1 (Ausnahmehandler) - wrapper_0) & 0xffff dw 0x0008 Interrupt-Gate dw 0x8e00 (Ausnahmehandler + cli) dw ((wrapper_%1 - wrapper_0) & 0xffff0000) >> 16 %endmacro setup_idt: mov eax,wrapper_0 ; ax: niederwertige 16 Bit ;... wird automatisch erzeugt. mov ebx,eax shr ebx,16 ; bx: hoeherwertige 16 Bit %assign i 0 mov ecx,256 ; Zaehler %rep 256.loop: add [idt+8*ecx+0],ax idt_entry i adc [idt+8*ecx+6],bx %assign i i+1 IDTR dec ecx %endrep jge.loop idt_descr: lidt [idt_descr] dw 256*8-1 ; idt enthaelt 256 Eintraege ret dd idt 30.10.12 Betriebssystembau: 3. Übung 23
Zustandssicherung Jede CPU besitzt einen internen Status, der nur einmal vorhanden ist repräsentiert als Registerinhalte Bei x86-prozessoren sieht dieser wie folgt aus: Komplette Sicherung (Stack): 14 Register == 56 Bytes! 30.10.12 Betriebssystembau: 3. Übung 24
Beispiel (Linux arch/i386/kernel/entry.s) Implementation als Makro... wird häufiger benötigt :-) RESTORE_ALL entsprechend 87 #define SAVE_ALL \ 88 cld; \ 89 pushl %es; \ 90 pushl %ds; \ 91 pushl %eax; \ 92 pushl %ebp; \ 93 pushl %edi; \ 94 pushl %esi; \ 95 pushl %edx; \ 96 pushl %ecx; \ 97 pushl %ebx; \ 98 movl $( KERNEL_DS),%edx; \ 99 movl %edx,%ds; \ 100 movl %edx,%es; 30.10.12 Betriebssystembau: 3. Übung 25
Beispiel (Linux arch/i386/kernel/entry.s) Verwendung z.b. im NMI-Handler: 332 ENTRY(nmi) 333 pushl %eax 334 SAVE_ALL 335 movl %esp,%edx 336 pushl $0 337 pushl %edx 338 call SYMBOL_NAME(do_nmi) 339 addl $8,%esp 340 RESTORE_ALL 30.10.12 Betriebssystembau: 3. Übung 26
Kontextsicherung: Wer macht was? Kontextsicherung: eflags, cs und eip wurden bereits von der CPU gesichert alle weiteren Register müssen vom IRQ-Handler gesichert werden entweder im Assembler-Teil oder der Compiler generiert bereits entsprechenden Code Kontextsicherung beim Aufruf von Funktionen Lösung 1: Aufrufende Funktion sichert alle Register, die sie später noch braucht Lösung 2: Aufgerufene Funktion sichert alle Register, die sie verändert Lösung 3: Ein Teil der Register wird vom Aufrufer, ein anderer Teil vom Aufgerufenen gesichert 30.10.12 Betriebssystembau: 3. Übung 27
Kontextsicherung in Hochsprachen In der Praxis wird Lösung 3 verwendet Grundsätzlich hängt das natürlich vom Compiler ab CPU-Hersteller definiert jedoch Konventionen, damit Interoperabilität auf Binärcodeebene sichergestellt ist Register werden in 2 Subsets aufgeteilt Flüchtige Register ( scratch registers ) Compiler geht davon aus, dass Unterprogramm den Inhalt verändert Aufrufer muss Inhalt gegebenenfalls sichern bei x86 sind eax, ecx, edx und eflags als flüchtig definiert Nichtflüchtige Register ( non-scratch registers ) Compiler geht davon aus, dass der Inhalt nicht verändert wird Aufgerufene Funktion muss Inhalt gegebenenfalls sichern bei x86 sind alle sonstigen Register als nicht-flüchtig definiert Interrupt-Handler müssen auch flüchtige Register sichern! 30.10.12 Betriebssystembau: 3. Übung 28
Zustandssicherung im Wrapper ; Spezifischer Kopf der Unterbrechungsbehandlungsroutinen %macro wrapper 1 wrapper_%1: push eax mov al,%1 jmp wrapper_body %endmacro ;... wird automatisch erzeugt. %assign i 0 %rep 256 wrapper i %assign i i+1 %endrep ; Gemeinsamer Rumpf wrapper_body: cld ; das erwartet der gcc so. push ecx ; Sichern der fluechtigen Register push edx and eax,0xff ; Der generierte Wrapper liefert nur 8 Bits push eax ; Nummer der Unterbrechung uebergeben call guardian add esp,4 ; Parameter vom Stack entfernen pop edx ; fluechtige Register wieder herstellen pop ecx 30.10.12 pop eax iret Betriebssystembau: ; fertig! 3. Übung 29
Initialisierung der PICs Teil 1 OO-Stubs Einstellung: Master Slave 00010001 00010001 OO-Stubs Einstellung: Master Slave 00100000 00101000 30.10.12 Betriebssystembau: 3. Übung 30
Initialisierung der PICs Teil 1 OO-Stubs Einstellung: Master Slave ; Neuprogrammierung der PICs (Programmierbare 00010001 Interrupt-Controller), 00010001 ; damit alle 15 Hardware-Interrupts nacheinander in der idt liegen. reprogram_pics: mov al,0x11 ; ICW1: 8086 Modus mit ICW4 out 0x20,al call delay out 0xa0,al call delay OO-Stubs Einstellung: mov al,0x20 ; ICW2 Master: IRQ Master # Offset (32) Slave out 0x21,al call delay 00100000 00101000 mov al,0x28 ; ICW2 Slave: IRQ # Offset (40) out 0xa1,al call delay 30.10.12 Betriebssystembau: 3. Übung 31
Mapping der HW-IRQs (OO-Stubs) 0 32 47 255 IDT Traps HW <unbenutzt> Standard AT IRQ-Belegung IRQ 0 IRQ 1 IRQ 2 System Timer IRQ 3 COM 2 IRQ 4 COM 1 IRQ 5 Tastatur (Keyboard) PIC Kaskadierung IRQ 6 Floppy IRQ 7 LPT 1 IRQ 8 CMOS Echtzeituhr IRQ 9 (HW-Mapping von IRQ 2) IRQ10 IRQ11 IRQ12 IRQ13 numerischer Coprozessor IRQ14 1. IDE Port 30.10.12 Betriebssystembau: 2. Übung 32 IRQ15 2. IDE Port PS/2
Initialisierung der PICs Teil 2 OO-Stubs Einstellung: Master Slave 00000100 0000010 OO-Stubs Einstellung: Master Slave 00000011 00000011 30.10.12 Betriebssystembau: 2. Übung 33
Initialisierung der PICs Teil 2 OO-Stubs Einstellung: Master Slave... mov al,0x04 ; ICW3 Master: Slaves 00000100 an IRQs 0000010 out 0x21,al call delay mov al,0x02 ; ICW3 Slave: Verbunden mit IRQ2 des Masters out 0xa1,al call delay mov al,0x03 ; ICW4: 8086 Modus und automatischer EIO out 0x21,al call delay OO-Stubs Einstellung: out 0xa1,al Master Slave call delay... 00000011 00000011 30.10.12 Betriebssystembau: 2. Übung 34
Programmierung der PICs Interruptmaske (IMR) schreiben und lesen über Port 0x21 / 0xa1 30.10.12 Betriebssystembau: 2. Übung 35
Programmierung der PICs Interruptmaske (IMR) schreiben und lesen über Port 0x21 / 0xa1... mov al,0xff ; Hardware-Interrupts durch PICs out 0xa1,al ; ausmaskieren. Nur der Interrupt 2, call delay ; der der Kaskadierung der beiden mov al,0xfb ; PICs dient, ist erlaubt. out 0x21,al ret 30.10.12 Betriebssystembau: 2. Übung 36
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 37
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 38
Interrupthandler in OO-Stubs // ASSIGN: Einstoepseln einer Behandlungsroutine, die in Form eines Gate- // Objekts angegeben wird. void assign (unsigned int slot, Gate& gate); // REPORT: Abfrage eines eingetragenen Gate Objekts. Gate& report (unsigned int slot); 30.10.12 Betriebssystembau: 3. Übung 39
Was sind C++ Referenzen? Sprachlich: Aliase für Objekte Technisch: Initialisierte und unveränderliche Zeiger Wenn eine Referenz initialisiert wird, wird automatisch die Adresse des Initialisierers gebildet. Wenn eine Referenz in einem Ausdruck benutzt wird, wird automatisch das referenzierte Objekte benutzt. int v1; int &ref = v1; int v2 = ref; int &f(int &refarg) { refarg = 42; return refarg; } int v1; int *ref = &v1; int v2 = *ref; int *f(int *refarg) { *refarg = 42; return &*refarg; } int v3 = f(v2); int v3 = *f(&v2); technisch äquivalent 30.10.12 Betriebssystembau: 3. Übung 40
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 41
Aufgabe 3: Pro-/Epilog-Modell Die Hauptarbeit steckt in Guard Das Modell wird ausführlich in der kommenden Vorlesung besprochen. 30.10.12 Betriebssystembau: 3. Übung 42
Knifflige Zeiger: Queue in Aufgabe 3 Queue-Elemente erben von Chain Sie erben damit auch einen Zeiger auf das nächste Element Ein Queue-Objekte enthält class Chain { public: Chain* next; }; einen Zeiger auf das erste Element einen Zeiger auf einen Zeiger namens 'tail'!? class Queue { Chain* head; Chain** tail; public: Queue () { head = 0; tail = &head; } void enqueue (Chain* item); Chain* dequeue (); void remove (Chain*); }; 30.10.12 Betriebssystembau: 3. Übung 43
Knifflige Zeiger: Queue in Aufgabe 3 'tail' ist ein Zeiger auf den 'next' Zeiger im letzten Element Das macht das Anhängen (enqueue) sehr einfach: q.enqueue(&e1) item->next = NULL; q NULL e1 'a' NULL e2 'b'? *tail = item; 'a' NULL 'b'? tail = &item->next; 'a' NULL 'b'? q.enqueue(&e2)... item->next = NULL; *tail = item; tail = &item->next; 30.10.12 Betriebssystembau: 3. Übung 44 'a' 'b' NULL
Agenda Traps Unterbrechungssynchronisation Aufgabe 2 Was macht startup.asm für uns? Was soll in Teil c) und d) passieren? Wiederholung: Referenzen in C++ Plugbox und Gate Aufgabe 3 Vielen Dank für die Aufmerksamkeit! Vorstellung Wiederholung: Zeiger in C++ Die (komische?) Queue-Klasse 30.10.12 Betriebssystembau: 3. Übung 45