- 论坛徽章:
- 0
|
[从"处理器间中断-发送"中的描述可知]:
#define IPI_BITMAP_VECTOR 249
#define IPI_PREEMPT 1
1:已经给cpu5发送了一个中断向量号为IPI_BITMAP_VECTOR的IPI。
2:并且数组元素cpu_ipi_pending[5]的第IPI_PREEMPT个bit位被设置为1。
[从"内核如何实现IA32中断处理"中的描述可知]:
中断向量号为IPI_BITMAP_VECTOR对应的中断处理程序为函数IDTVEC(ipi_intr_bitmap_handler)。
此时,cpu5接收到了这个IPI,在执行中断处理程序IDTVEC(ipi_intr_bitmap_handler)前,cpu硬件单元要做一些处理,硬件单元所做的处理可以参考
"内核如何实现IA32中断处理"结尾的描述。
这里做下面的假设:
1:接收到这个IPI时,cpu5运行在用户态。
2:接收到这个IPI时,正在cpu5运行的线程对应的struct thread对象简记为cur_thread。
3:cpu硬件单元已经完成了在执行中断处理程序之前的处理。
此时cpu5运行在内核态,开始执行中断处理程序IDTVEC(ipi_intr_bitmap_handler),线程cur_thread的内核栈如下所示:- ******************************* 内核栈顶部 高地址方向
- * *
- * struct pcb对象 *
- * *
- * *
- ******************************* <--cur_thread的td_pcb成员指向这里
- * *
- * 16bytes for vm *
- ******************************* <--cur_thread的内核栈栈顶
- * SS *
- *******************************
- * ESP *
- *******************************
- * EFLAGS *
- *******************************
- * CS *
- *******************************
- * EIP *
- ******************************* <--执行中断处理程序IDTVEC(ipi_intr_bitmap_handler)时,esp指向这里
- * SS *
- *******************************
- * 0 *
- *******************************
- * 0 *
- *******************************
- * EAX *
- *******************************
- * ECX *
- *******************************
- * EDX *
- *******************************
- * EBX *
- ******************************* 通过struct trapframe类型的数据对象来描述
- * ESP *
- *******************************
- * EBP *
- *******************************
- * ESI *
- *******************************
- * EDI *
- *******************************
- * DS *
- *******************************
- * ES *
- *******************************
- * FS *
- ******************************* <--宏PUSH_FRAME和SET_KERNEL_SREGS执行完后,esp指向这里
- * addr(jmp doreti) *
- ******************************* <-- 执行call ipi_bitmap_handler指令时esp指向这里
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- *******************************------ 内核栈底部 低地址方向
复制代码 [IDTVEC(ipi_intr_bitmap_handler)]-中断向量号IPI_BITMAP_VECTOR对应的中断处理程序:- 304 IDTVEC(ipi_intr_bitmap_handler)
- /*******************************************************************************
- * 305,宏PUSH_FRAME,该宏如下所示:
- 将线程cur_thread的用户态硬件上下文的一部分压入内核栈中。
- 306,宏SET_KERNEL_SREGS,如下所示:
- 设置内核使用的段寄存器。
- 307:
- 清DF标志,string operations increment the index registers (ESI and/or EDI).
- ******************************************************/
- 305 PUSH_FRAME
- 306 SET_KERNEL_SREGS
- 307 cld
- 308
- /*******************************************************************
- * 309-310:
- 通过lapic访问cpu5的local APIC寄存器组。
- 这里将对local APIC寄存器组中的EOI Register执行一个写操作,这是
- APIC要求的,作为对这个写操作的响应,cpu将:
- Upon receiving an EOI, the APIC clears the highest
- priority bit in the ISR and dispatches the next highest
- priority interrupt to the processor
- **********************************************/
- 309 movl lapic, %edx
- 310 movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */
- 311
- 312 FAKE_MCOUNT(TF_EIP(%esp))
- 313
- /**********************************************************************
- * 314:
- 通过上面内核栈的布局,可以明显的看出,函数ipi_bitmap_handler
- 的参数是一个struct trapframe类型的对象,而不是指向这个对象的指针。
- 316:
- doreti函数完成返回中断前的处理,这个暂时略过。
- *********************************/
- 314 call ipi_bitmap_handler
- 315 MEXITCOUNT
- 316 jmp doreti
- 317 #endif
复制代码 [宏PUSH_FRAME]:- /*****************************************************************************************
- * Macros to create and destroy a trap frame.
- pushal指令:
- Pushes the contents of the general-purpose registers onto the stack. The registers
- are stored on the stack in the following order: EAX, ECX, EDX, EBX, ESP (original
- value), EBP, ESI, and EDI (if the current operand-size attribute is 32)。
- *********************************************/
- 145 #define PUSH_FRAME \
- 146 pushl $0 ; /* dummy error code */ \
- 147 pushl $0 ; /* dummy trap type */ \
- 148 pushal ; /* 8 ints */ \
- 149 pushl %ds ; /* save data and extra segments ... */ \
- 150 pushl %es ; \
- 151 pushl %fs
复制代码 [宏SET_KERNEL_SREGS]:- /**********************************************************************************
- * Setup the kernel segment registers.
- 此时cpu5运行在内核态,段寄存器也要更改。
-
- 173-175:
- 用内核数据段的segment selector,即KDSEL加载DS,ES两个寄存器,访问内核数据。
- 176-177:
- 用KPSEL加载FS寄存器,这样就能访问数据对象__pcpu[5],也是宏PCPU_*的基础。
- ******************************************/
- 172 #define SET_KERNEL_SREGS \
- 173 movl $KDSEL, %eax ; /* reload with kernel's data segment */ \
- 174 movl %eax, %ds ; \
- 175 movl %eax, %es ; \
- 176 movl $KPSEL, %eax ; /* reload with per-CPU data segment */ \
- 177 movl %eax, %fs
复制代码 [函数ipi_bitmap_handler]:- /***********************************************************************
- * 参数描述:
- frame:用来访问保存在线程cur_thread内核栈上用户态硬件上下文。
- ****************************/
- 1355 void
- 1356 ipi_bitmap_handler(struct trapframe frame)
- 1357 {
- /***************************************************************************
- * 1360,cpu:PCPU_GET宏获取当前cpu的logical cpu id,这里为5.
- *******************************/
- 1358 struct trapframe *oldframe;
- 1359 struct thread *td;
- 1360 int cpu = PCPU_GET(cpuid);
- 1361 u_int ipi_bitmap;
- /**************************************************************************
- * 1364:td指向当前正在运行线程的struct thread对象,这里为&cur_thread。
- 1365:
- 递增td_intr_nesting_level,即递增中断嵌套层数。
- 1366-1367:
- struct thread对象的td_intr_frame成员,freebsd8.3中没有该成员,添加的
- 这个成员个人理解为,该成员在中断处理程序中使用,用来访问保存在线程
- 内核栈上的硬件上下文。
- 1368:
- 函数atomic_readandclear_int返回数组元素cpu_ipi_pending[5]的值,然后
- 将其清零。
- *****************************/
- 1363 critical_enter();
- 1364 td = curthread;
- 1365 td->td_intr_nesting_level++;
- 1366 oldframe = td->td_intr_frame;
- 1367 td->td_intr_frame = &frame;
- 1368 ipi_bitmap = atomic_readandclear_int(&cpu_ipi_pending[cpu]);
- /****************************************************************************
- * 三个特殊的IPI如下:
- * #define IPI_AST 0
- #define IPI_PREEMPT 1
- #define IPI_HARDCLOCK 2
- * 1369-1386:
- 依次测试变量的ipi_bitmap的第1,0,2个比特位,检查发送
- 的是上面三个特殊IPI的哪一个,并递增相应的计数器。
- 1369-1374:
- 发送的是IPI_PREEMPT,此时调用调度程序相关的sched_preempt函数,当
- sched_preempt函数执行完后,cpu5上的线程cur_thread被成功抢占。
- 1375-1380:
- 发送的是IPI_AST,此时不做处理。
- 1381-1386:
- 发送的是IPI_HARDCLOCK,此时调用函数hardclockintr。
- ******************************************************/
- 1369 if (ipi_bitmap & (1 << IPI_PREEMPT)) {
- 1370 #ifdef COUNT_IPIS
- 1371 (*ipi_preempt_counts[cpu])++;
- 1372 #endif
- 1373 sched_preempt(td);
- 1374 }
- 1375 if (ipi_bitmap & (1 << IPI_AST)) {
- 1376 #ifdef COUNT_IPIS
- 1377 (*ipi_ast_counts[cpu])++;
- 1378 #endif
- 1379 /* Nothing to do for AST */
- 1380 }
- 1381 if (ipi_bitmap & (1 << IPI_HARDCLOCK)) {
- 1382 #ifdef COUNT_IPIS
- 1383 (*ipi_hardclock_counts[cpu])++;
- 1384 #endif
- 1385 hardclockintr();
- 1386 }
- /********************************************************
- * 1387:将td_intr_frame成员恢复到之前的值。
- 1388:递减td_intr_nesting_level,即中断嵌套数减1.
- *******************************/
- 1387 td->td_intr_frame = oldframe;
- 1388 td->td_intr_nesting_level--;
- 1389 critical_exit();
- 1390 }
复制代码 [ULE线程调度-sched_preempt函数]:- /*******************************************************************
- * 函数sched_preempt就是调用mi_switch函数执行一个线程切换,执行
- 完后,线程cur_thread被成功抢占。
- 2130:tdq此时为&tdq_cpu[5].
- 2132:将tdq_ipipending设置为0,表示相应的IPI已经处理。
- 2133-2143:
- 再次检查是否需要抢占线程cur_thread,这里简单的比较两个优先级。
- 如果需要抢占,就设置合适的标志来调用mi_switch函数完成线程切换。
- ***********************************/
- 2122 void
- 2123 sched_preempt(struct thread *td)
- 2124 {
- 2125 struct tdq *tdq;
- 2126
- 2127 SDT_PROBE2(sched, , , surrender, td, td->td_proc);
- 2128
- 2129 thread_lock(td);
- 2130 tdq = TDQ_SELF();
- 2131 TDQ_LOCK_ASSERT(tdq, MA_OWNED);
- 2132 tdq->tdq_ipipending = 0;
- 2133 if (td->td_priority > tdq->tdq_lowpri) {
- 2134 int flags;
- 2135
- 2136 flags = SW_INVOL | SW_PREEMPT;
- 2137 if (td->td_critnest > 1)
- 2138 td->td_owepreempt = 1;
- 2139 else if (TD_IS_IDLETHREAD(td))
- 2140 mi_switch(flags | SWT_REMOTEWAKEIDLE, NULL);
- 2141 else
- 2142 mi_switch(flags | SWT_REMOTEPREEMPT, NULL);
- 2143 }
- 2144 thread_unlock(td);
- 2145 }]
复制代码 |
|