自旋死锁与抢占

0.
做个内核死锁的小实验,顺带接触下内核抢占。
1. 单核 + 抢占
实验很简单,将以下代码编译成模块插入内核(两次自旋加锁导致死锁)
DEFINE_SPINLOCK(lock);
printk(KERN_ALERT "dead lock :(/n");
spin_lock(&lock);
spin_lock(&lock);
结果:此时按键后,终端没有响应。
但是终端(用户进程)不响应,不代表(按键)中断没有被内核响应。
继续测试下内核是否响应中断。
2. 单核 + 抢占
由于我的虚拟机模拟的是AT键盘,所以对AT键盘中断响应函数稍作修改以便调试:
+ int __enable_debug = 0;
+ EXPORT_SYMBOL(__enable_debug);
static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
unsigned int flags)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
struct input_dev *dev = atkbd->dev;
unsigned int code = data;
int scroll = 0, hscroll = 0, click = -1;
int value;
unsigned short keycode;
+ if (__enable_debug)
+ printk(KERN_ALERT "in atkbd_interrupt/n");
dev_dbg(&serio->dev, "Received %02x flags %02x/n", data, flags);

重新编译内核,然后修改之前死锁的模块代码.
extern int __enable_debug;
DEFINE_SPINLOCK(lock);
+ __enable_debug = 1;
printk(KERN_ALERT "dead lock :(/n");
spin_lock(&lock);
spin_lock(&lock);

结果:此时按键后,终端显示 "in atkbd_interrupt", 但是这个并不是用户态进程输出的。
(调试还有其他方法,重新编译内核的方法最简单暴力)

3. 多核 + 抢占
以下实在真机上测试,将之前代码插入内核运行,当前终端仍能响应按键,并且开启其他终端
仍然可以工作

4.
其实原理也相对的简单,在开启抢占的内核(CONFIG_PREEMPT=y),
spin_lock内部会调用diable_preempt()来关闭抢占。
这样在中断返回(ret_from_intr)时, 内核会检测是否关闭抢占,如果关闭,就不会试图去
调度用户太进程(即用户太进程没有机会抢占内核,终端也不会对用户输入流进行响应)
这里的意思是用户态进程有机会抢占内核,但这个机会在自旋加锁后就没有了。

多核情况下,由于代码是单核死锁,另外一个核仍能正常工作响应用户需求。

 

你可能感兴趣的:(工作,虚拟机,struct,测试,终端,scroll)