Absrtact: Looking back, we found that our interrupt handlers were not written elegantly enough. The interrupt response requires three conditions: the interrupt token in the eflags is open, the interrupt mask register does not block the corresponding interrupt, and the EOI flag is set.
1. Modify the clock interrupt handler: G/kernel/kernel.asm
174 Inc DWORD [K_reenter]
175 CMP DWORD [K_reenter], 0
176 jne. 1; re-entry. 1
177
178 mov ESP, stacktop ; cut to kernel stack
179
push . RESTART_V2
jmp . 2
. 1:
push . Restart_ Reenter_v2
. 2 sti
181
182 push 0 183 call Clock_handler
184 Add ESP, 4
185
186 CLI
ret, corresponding to the preceding push statement
. RESTART_V2:
188 mov ESP , [P_proc_ready]; leaving the kernel stack;
189 Lldt [esp + p_ldt_sel] eax, [esp + p_stacktop]
191 mov DWORD [TSS + TSS3_S_SP0 ], eax
192
193. RESTART_REENTER_V2: if (k_reenter! = 0), it will jump here
194 Dec DWORD [K_reenter] ; k_reenter--;
195 pop GS ; ┓
196 pop fs ┃
197 pop es ; ┣ Restore original Register value
198 Pop ds ; ┃
199 popad ; ┛ add ESP, 4
201
Note: The above code has also changed logically: The interrupt service is still executed when the interrupt is re-entered, but it will only print ". "is returned; a process switch is performed without an interrupt switch.
Let's take a look at the corresponding interrupt handlers:
-public void Clock_handler (int IRQ),
{ disp_str ("#"); ticks++; if (k_reenter! = 0) { disp_str ("!"); return; p_proc_ready++; if (p_proc_ready >= proc_table + nr_tasks) {p_proc_ready = proc_table; {
36}
After we change the code, let's Run the program again:
Think: Whether the above code will cause a stack overflow problem caused by interrupt re-entry.
2. Modify the Restart programRestart is a program that re-runs the Sleep program, which is defined by the assembly in Kernel.asm, called by Tnix_main () in Main.c, and then enters the dead loop:
k_reenter =-1; P_proc_ready = proc_table; Restart (); (1) {}
Let's change the code for restart:
353 Restart:
354 mov esp, [p_proc_ready]
355 Lldt [esp + P_ldt_sel]
356 lea EAX, [ESP + P_ Stacktop]
357 mov DWORD [TSS + tss3_s_sp0], eax
358 restart_reenter:
Dec DWORD [K_reenter]; Because of this sentence, We need to change the initial value of K_reenter from 1 to 0
359 pop gs
pop fs
361 pop es
362 pop ds
363 Popad
364 add ESP, 4
365 iretd
Since the latter part of the clock interrupt is the same as the restart, we can omit the coincident part of the clock interrupt.
3 Modifying the interrupt handler--saveCompare the original interrupt handler: Let's summarize the contents of the clock Interrupt program:
1) Protection context
2) Judging interrupt re-entry and stack switching
3) Open interrupt and interrupt processing core program
4) Restore context (already and in the restart process)
Now, we will be 1 and 2) also Independent:
193 Save:
194 Sub ESP, 4
195 Pushad ; ┓
196 push ds ; ┃
197 Push Es ; ┣ Save original Register value
198 push FS ; ┃
199 push GS ; ┛
. mov dx, ss
201 mov ds, DX
202 mov es, dx
203
204 mov eax,esp
205
206 Inc DWORD [K_reenter]
207 CMP DWORD [K_reenter], 0
208 jne. 1
209 with mov esp, Stacktop ; cut to the kernel stack
211 push restart
212 jmp. 2
213.1:
214 Push Restart_reenter
215.2:
216 jmp [Eax+retadr-p_stackbase]
Partial content of the interrupt handler:
158 hwint00: ; Interrupt routine for IRQ 0 (the clock).
159 ; Inc Byte [gs:0]; Change the No. 0 row of the screen, the character of the No. 0 column
161 call Save
162 mov al, EOI ; ┓re Enable Master 8259
163 out Int_m_ctl, Al ; ┛
Here, we notice a very strange call command--not ending with a ret. Why is it. Think about it, ret pops the return address from the stack to the EIP; but because of this, ESP is changed in the call instruction, so it can't be returned with RET, it needs to use JMP, which is the address of the next instruction to call as the return address of the calling procedure.
4. Modify interrupt Handler--reentrant problem of clock interruptAs we have already analyzed in the first part of this article, the code in this case can still cause a re-entry of the clock interrupt: Therefore, it is necessary to turn off the clock interrupt before reopening the interrupt, and then turn the clock interrupt on after the interrupt is closed.
Look at the code:
Save
161 162 in al,int_m_ctlmask
163 or al,1 164 out int_m_ Ctlmask,al
165
166 mov al, EOI ; ┓reenable Master 8259
167 out Int_m_ctl, Al ; ┛
168
169 STI 171 push 0
call clock_handler
172 Add ESP, 4
173
174 CLI
175 in al,int_m_ctlmask
176 and Al,0xfe
177 Out int_m_ctlmask
178
179 RET
5. Modify interrupt Handlers-code modularity: Unified Interrupt Processing RoutinesIn the above code, we notice that the real clock-related parts are finite, which can easily be extended to other interrupt types. Let's define a macro hwint_master to handle hardware interrupts.
150; Interrupts and exceptions--hardware interrupt
151;---------------------------------
%macro hwint_master 1
153 Call Save
154 in AL, Int_m_ctlmask ; ┓
155 or al, (1 <<% 1) ; ┣ Mask Current interrupt
156 out Int_m_ctlmask, Al ; ┛
157 mov al, EOI; ┓ EOI bit 158 out Int_m_ctl, al< c23/>; ┛
159 STI; The CPU automatically shuts down interrupts during the response to the interrupt, which is then allowed to respond to the new interrupt push %1 ; ┓
161 call [irq_table + 4 *%1] ; ┣ interrupt handler; Obviously, Irq_table is an array of function pointers, which we define in GLOBAL.C
162 pop ecx ; ┛
163 CLI
164 in al, Int_m_ctlmask ; ┓
165 and Al, ~ (1 << 1) ; ┣ Resume Accept current interrupt
166 Out Int_m_ctlmask, Al ; ┛
167 ret
168%endmacro
Related:
Irq_table is defined in the GLOBAL.C:
Public Irq_hander IRQ_TABEL[NR_IRQ];
Declaration: extern Irq_hander irq_table[]; in global.h
Increased definition of Irq_handler in type.h: typedef void (*irq_handler) (int IRQ)
Const.h: #define NR_IRQ 16
Next, let's initialize the irq_table and initialize it to SPURIOUS_IRP first:
In the init_8259a function:
int i; for
(I=0;I<NR_IRQ;I++LL)
Irq_table[i]=surious_irq;
Now, we need a function to assign a value to Irq_table[0]:
Put_irq_handler public void (int IRQ, T_pf_irq_handler handler)
{ DISABLE_IRQ (IRQ); This sentence is necessary Before interrupt processing, first off corresponding interrupt IRQ_TABLE[IRQ] = handler;
48}
Here, as you can see, we used an interrupt enable function DISABLE_IRQ, and we'll look at it and the structure of another function Enable_irq.
123; ======================================================================== 124;
void Disable_irq (int IRQ); 125; ======================================================================== 126;
Disable an interrupt request line by setting a 8259 bit. 127; Equivalent code for IRQ < 8:128; Out_byte (Int_ctlmask, In_byte (int_ctlmask) |
(1 << IRQ)); 129;
Returns true iff the interrupt is not already disabled.
130; 131 disable_irq:132 mov ecx, [esp + 4]; IRQ 133 PUSHF 134 CLI 135 mov ah, 1 136 rol Ah, cl; Ah = (1 << (IRQ% 8)) 137 CMP cl, 8 138 Jae Disable_8; Disable IRQ >= 8 at the slave 8259 139 disable_0:140 in AL, Int_m_ctlmask 141 test Al, ah 142 jnz Dis_ already;
Already disabled? 143 or Al, ah 144 out Int_m_ctlmask, AL; Set bit at Master 8259 145 Popf 146 mov eax, 1; Disabled by this function 147 RET 148 disable_8:149 in AL, Int_s_ctlmask-Test Al, ah 151 jnz dis_already;
Already disabled? Or AL, ah 153 out Int_s_ctlmask, AL; Set bit at slave 8259 154 Popf 155 mov eax, 1; Disabled by this function 156 RET 157 dis_already:158 Popf 159 xor eax, EAX; Already disabled RET 161
The corresponding enable code we omit, please read the reader on their own to explain.
6. Application of a unified interrupt processing paradigm
In MAIN.C, the interrupt handler is bound to the pinned pin.
P_proc_ready = proc_table; Put_irq_handler (CLOCK_IRQ, clock_handler); /* Set Clock Interrupt handler * /ENABLE_IRQ (CLOCK_IRQ); /* Allow 8259A to receive clock interrupts
* /restart ();
Block all interrupts in init_8259a: Out_byte (INT_M_CTLMASK,0XFF).
To sum up, how we paradigm the interrupt processing process: We extract the register recovery, the stack switch LDT, and so on to the restart part; interrupt processing core: printing related information and process switching is put into the clock interrupt processing handle; Save the context, The judgment interrupt re-entry is put into the save function; In order to prevent clock interrupt re-entry, we add interrupt enable control statement in interrupt processing process; Next, interrupt processing is extended to generalized interrupt processing to encapsulate the interrupt processing process into macro definition Hwint_master. The process is roughly the same, as you can see, it's a process from specialization to generalization, and it's a good way to avoid bloated code and mistakes.