关于进程上下文和中断上下文区别定义什么的,网上一搜一大把,但是我关心的是,内核是如何实现这种区分的。看了半天源码(不是最新的),得出结果如下:
首先在内核初始化的时候,会调用init_IRQ来初始化中断描述符表中,关于中断的部分,部分代码如下:
458 for (i = 0; i < NR_IRQS; i++) {
459 int vector = FIRST_EXTERNAL_VECTOR + i;
460 if (vector != SYSCALL_VECTOR)
461 set_intr_gate(vector, interrupt[i]);
462 }
可见,对于特定中断,中断描述符表中存放的函数指针为interrupt[i].那么该函数指针数组又是哪里定义的呢?
101 #define IRQ(x,y) \
102 IRQ##x##y##_interrupt
103
104 #define IRQLIST_16(x) \
105 IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
106 IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
107 IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
108 IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
109
110 void (*interrupt[NR_IRQS])(void) = {
111 IRQLIST_16(0x0),
112
113 #ifdef CONFIG_X86_IO_APIC
114 IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3),
115 IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
116 IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
117 IRQLIST_16(0xc), IRQLIST_16(0xd)
118 #endif
119 };
通过宏IRQLIST_16(nr)来生成对应的函数指针,最终的函数指针形式像这样:IRQ0x00_interrupt,IRQ0x01_interrupt…
那么这些函数又是在哪里定义的呢?
113 #define IRQ_NAME2(nr) nr##_interrupt(void)
114 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
...
175 #define BUILD_IRQ(nr) \
176 asmlinkage void IRQ_NAME(nr); \
177 __asm__( \
178 "\n"__ALIGN_STR"\n" \
179 SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
180 "pushl $"#nr"-256\n\t" \
181 "jmp common_interrupt");
通过BUILD_IRQ(nr)宏来生成对应的函数。
而这些BUILD_IRQ宏在编译阶段静态实现:
141 BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */
142 BUILD_IRQ(3, 0x08)
143 BUILD_IRQ(4, 0x10)
144 BUILD_IRQ(5, 0x20)
145 BUILD_IRQ(6, 0x40)
146 BUILD_IRQ(7, 0x80)
147 BUILD_IRQ(8, 0x100)
148 BUILD_IRQ(9, 0x200)
149 BUILD_IRQ(10, 0x400)
150 BUILD_IRQ(11, 0x800)
151 BUILD_IRQ(12, 0x1000)
152 BUILD_IRQ(13, 0x2000)
153 void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */
154 void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */
155 BUILD_IRQ(16, 0x10000)
156 BUILD_IRQ(17, 0x20000)
157 BUILD_IRQ(18, 0x40000)
158 BUILD_IRQ(19, 0x80000)
159 BUILD_IRQ(20, 0x100000)
160 BUILD_IRQ(21, 0x200000)
161 BUILD_IRQ(22, 0x400000)
162 BUILD_IRQ(23, 0x800000)
163 BUILD_IRQ(24, 0x1000000)
164 BUILD_IRQ(25, 0x2000000)
165 /* IRQ 26-30 are reserved */
166 BUILD_IRQ(31, 0x80000000)
而每个IRQ0x**_interrupt最终调用的都是函数do_IRQ。而该函数会调用preempt_count_add(HARDIRQ_OFFSET)来标识当前内核处于中断上下文。
对于系统调用,在trap_init中写入中断描述符表,
986 set_system_gate(SYSCALL_VECTOR,&system_call);
对应的函数指针为system_call,对应的函数定义为:
202 ENTRY(system_call)
203 pushl %eax # save orig_eax
204 SAVE_ALL
205 GET_CURRENT(%ebx)
206 testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
207 jne tracesys
208 cmpl $(NR_syscalls),%eax
209 jae badsys
210 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
211 movl %eax,EAX(%esp) # save the return value
212 ENTRY(ret_from_sys_call)
213 cli # need_resched and signals atomic test
214 cmpl $0,need_resched(%ebx)
215 jne reschedule
216 cmpl $0,sigpending(%ebx)
217 jne signal_return
218 restore_all:
219 RESTORE_ALL
调用特定处理函数的地方为:
210 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
在系统调用表中查找对应系统调用号的处理函数。 而该处理函数集合没有调用preempt_count_add(HARDIRQ_OFFSET),因此当前内核处于进程上下文。 这就是为什么系统调用也属于中断(int 0x80),但是却属于进程上下文,而不属于中断上下文的原因。