前一段时间,在做一个驱动的压力测试时,发现一个问题。在硬件中断服务代码中,直接调用sys_kill()向进程发信号,可能会导致内核发生缺页错误,从而引发内核崩溃,Oops信息附在文末。

通过Oops信息可知,内核崩溃时,正在运行pid_vnr()这个函数,而该函数又是按照irq_srv()==>sys_kill()==>pid_vnr()关系被调用的。问题发生再pid_vnr()这个函数,这个函数位于内核的linux/pid.c中,代码如下

参数pid是在sys_kill()中,通过引用current宏传递的。即pid = current。从函数的代码中可以看出,该函数内容很简单,只是将上级传递的参数pid下传,并且将current->nsproxy->pid_ns求值后下传。

pid,也就是current是内核中定义的一个宏,用于指向当前调度的进程的信息(数据类型struct task_struct)。内核在这里提示缺页错误,那么说明要么在执行current->nsproxy操作时访问了非法指针,即current所指向的内存区域非法。要么在执行nsproxy->pid_namespace操作是访问了非法(页)指针,即current结构是完整的,但是current->nsproxy所指向的内存区域非法或者不完整,导致取其pid_namespace成员时访问非法(页)指针。

在网上搜了一下,没有找到先例。不过有个类似的先例,有人遇到了在中断服务函数中,求current->tgid导致缺页错误,同时经过他的测试,求current->pid也会如此。【这位仁兄提到的情况,经过我的验证(TBD:添加验证结果)】

由此,我们可以推断,在访问current的成员时就可能导致错误,所以不管nsproxy->pid_namespace操作是否非法,在硬件中断服务函数中,current结构本身是不完整的

而我们可以看到,所有用户进程发信号的kill()操作,最终都是通sys_kill()系统调用来实现的。至今为止,该系统调用一直都通过pid_vnr()来获取运行的进程相关信息,从而实现给指定进程发信号之外的扩展操作,比如给进程组发消息。所以在没有更多证据之前,我们可以认为,该调用是没有问题的。也说明该调用中中访问current是合法的、允许的。

既然该调用是没有问题的,那么为什么我们在中断服务程序中,可能会导致错误呢?解释只有一个,那就是中断服务程序上下文和用户程序系统调用的上下文不尽相同。

我们知道,Linux中的进程大致可以分为用户进程、内核线程(含idle)两类。而CPU中运行的代码,除了前面这两类外,还有可能就是内核代码(比如调度器代码),包括中断服务程序的代码。

系统调用发生在用户进程中,最后进入内核代码中完成。因此系统调用一定是有进程(包括用户进程或者内核线程)上下文的。换句话说,current在系统调用时一定是完整的、可用的。但是硬件中断服务函数的执行,却完全是异步的。硬件终端发生的时候,绝大多数时候,系统都在执行某种进程(用户进程、内核线程等),极少数情况下,可能在执行内核代码。这极少数情况下,内核可能正在保存原来的进程上下文(比如调度器在切换进程队列),也可能在执行没有进程上下文的操作(比如调度器完成原进程信息的保存后,尚未完成新进程信息的载入)。这种情况下,current结构明显是不可用的。这时候如果恰好中断中访问current,就可能导致错误。

那么如何避免这个错误呢?

从前面的分析很容易得出,内核缺页的根本原因,就是我们直接去访问有可能失效(尽管几率很小)的current结构。访问该结构只有一种情况下是必须的,那就是要给当前进程相关的进程发信号。由于硬件中断的随机性,我们这种需求基本上是不(应该)合理的。而从实践当中,我们常常也只需要向某个特定进程发信号。

所以解决方法就出来了,那就是不要通过current的机制获取进程信息。而是遍历系统的进程表,(通过PID)找到匹配的进程。代码如下:

这样,通过特定的进程信息(上文第6行,这里是通过PID,也可以是其他信息),找到进程。并执行操作,比如给该进程发信号:

附件——内核Oops信息:

Unable to handle kernel NULL pointer dereference at virtual address 00000010

pgd = c0004000

[00000010] *pgd=00000000

Internal error: Oops: 17 [#1] PREEMPT

Modules linked in:

CPU: 0 Not tainted (2.6.27.8 #59)

PC is at pid_vnr+0x20/0x54

LR is at sys_kill+0x40/0x178

pc : [] lr : [] psr: a0000093

sp : c1c67de0 ip : c1c67df0 fp : c1c67dec

r10: c1c65cd8 r9 : c1c66000 r8 : 00000027

r7 : 00000059 r6 : 00000000 r5 : c1c66000 r4 : 00000224

r3 : 00000000 r2 : 00000019 r1 : c1c1ada0 r0 : c1c1ada0

Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user

Control: 0005317f Table: 813c4000 DAC: 00000015

Process sleep (pid: 951, stack limit = 0xc1c66268)

Stack: (0xc1c67de0 to 0xc1c68000)

7de0: c1c67e9c c1c67df0 c03dbe6c c03e0844 00000027 00000000 00000000 c1c67e08

7e00: c03c8e9c c03c7b98 00000400 00400000 c1c67e34 00000001 c064c230 c1c4a3a0

7e20: 00000001 c12b9068 c1c67e5c c1c67e38 c03c9e64 c03c8dc0 00000000 80000093

7e40: bebf6000 c13abf30 c12b905c 00000000 c1c67e6c c1c67e60 c03c9ecc c03c9df8

7e60: c1c67e9c c1c67e70 c03c85f8 c03c9ec8 c1c67eac c132c2c0 00000000 00000000

7e80: 00000059 c0653644 c1c66000 c1c65cd8 c1c67eac c1c67ea0 c051f878 c03dbe3c

7ea0: c1c67ecc c1c67eb0 c03f4204 c051f864 c063dc88 c1c66000 00000059 c132c2c0

7ec0: c1c67ef4 c1c67ed0 c03f5714 c03f41d0 00000000 c063dc88 00000059 00000000

7ee0: 00000002 00000001 c1c67f14 c1c67ef8 c03b1048 c03f55e4 c1c27350 ffffffff

7f00: f4010000 02000000 c1c67f7c c1c67f18 c03b18c4 c03b1010 00000000 00000001

7f20: 00000010 60000013 00000011 c1c65ce0 c1c67f68 c1c65dc0 c1c14c40 00000000

7f40: c1c65cd8 c1c67f7c c12b9068 c1c67f60 c03c8e9c c03d19bc 60000013 ffffffff

7f60: 00000001 c1c65dd4 c1c67f68 c1c67f68 c1c67f94 c1c67f80 c03d1b64 c03d12c4

7f80: ffffffff 40149678 c1c67fa4 c1c67f98 c03d1bb4 c03d1ab8 00000000 c1c67fa8

7fa0: c03b1d40 c03d1bac 40149678 00000000 00000000 4014c0fc 00000000 00092e14

7fc0: 40149678 00000000 4014b1bc 4014b1bc 00000000 00000002 40024000 bebf5ed4

7fe0: 4014bd0c bebf5e38 40052c3c 400b83ac 60000010 00000000 00000000 00000000

Backtrace:

[] (pid_vnr+0x0/0x54) from [] (sys_kill+0x40/0x178)

[] (sys_kill+0x0/0x178) from [] (if_agc_irq+0x24/0x40)

[] (if_agc_irq+0x0/0x40) from [] (handle_IRQ_event+0x44/0x84)

[] (handle_IRQ_event+0x0/0x84) from [] (handle_edge_irq+0x140/0x1b0)

r7:c132c2c0 r6:00000059 r5:c1c66000 r4:c063dc88

[] (handle_edge_irq+0x0/0x1b0) from [] (__exception_text_start+0x48/0x64)

r8:00000001 r7:00000002 r6:00000000 r5:00000059 r4:c063dc88

[] (__exception_text_start+0x0/0x64) from [] (__irq_svc+0x44/0xd0)

Exception stack(0xc1c67f18 to 0xc1c67f60)

7f00: 00000000 00000001

7f20: 00000010 60000013 00000011 c1c65ce0 c1c67f68 c1c65dc0 c1c14c40 00000000

7f40: c1c65cd8 c1c67f7c c12b9068 c1c67f60 c03c8e9c c03d19bc 60000013 ffffffff

r6:02000000 r5:f4010000 r4:ffffffff

[] (do_exit+0x0/0x7f4) from [] (do_group_exit+0xbc/0xf4)

[] (do_group_exit+0x0/0xf4) from [] (sys_exit_group+0x18/0x20)

r4:40149678

[] (sys_exit_group+0x0/0x20) from [] (ret_fast_syscall+0x0/0x2c)

Code: e3c3303f e593300c e2501000 e5933230 (e5932010)

Kernel panic - not syncing: Fatal exception in interrupt