关于Linux内核源码中是如何区别进程上下文和中断上下文

本文深入探讨了Linux内核如何区分中断上下文和进程上下文,详细解析了中断描述符表的初始化过程,以及系统调用在中断描述符表中的特殊定位。通过理解中断和系统调用的上下文区别,有助于开发者更深入地掌握Linux内核的工作原理。

关于进程上下文和中断上下文区别定义什么的,网上一搜一大把,但是我关心的是,内核是如何实现这种区分的。看了半天源码(不是最新的),得出结果如下:
首先在内核初始化的时候,会调用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),但是却属于进程上下文,而不属于中断上下文的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值