might_sleep(): 指示当前函数可以睡眠。如果它所在的函数处于原子上下文(atomic context)中(如,spinlock, irq-handler…),将打印出堆栈的回溯信息。这个函数主要用来做调试工作,在你不确定不期望睡眠的地方是否真的不会睡眠时,就把这个宏加进去。
简单地说,如果没有调试的需求(绝大多数下你平常跑的系统都是release版本的kernel),那么这个宏(或者函数,称谓并不重要)什么实质性的活都不干,内核只是用它来做一件事,就是提醒你,调用该函数的函数可能会sleep,这个跟其名字也是匹配的: The function calling might_sleep() might sleep。如果你想看源码,我把它列在下面:
<include/linux/kernel.h>
点击(此处)折叠或打开
- # define might_resched() do { } while (0)
- # define might_sleep() do { might_resched(); } while (0)
不过如果有调试需求介入的话,比如你的系统莫名其妙地随机性地crash掉,在经过一段艰难的案情分析排查之后,最后你决定打开内核的 CONFIG_DEBUG_ATOMIC_SLEEP选项,那么此时might_sleep对案情的进一步推进就可能产生贡献了。 CONFIG_DEBUG_ATOMIC_SLEEP选项主要用来排查是否在一个ATOMIC操作的上下文中有函数发生sleep行为,关于什么是 ATOMIC操作,内核源码在might_sleep函数前也有一段注释:
this macro will print a stack trace if it is executed in an atomic context (spinlock, irq-handler, ...)
所以很明显,一个进程获得了spinlock之后它就进入了这里所谓的atomic context,或者是在一个irq-handler,也就是一个中断上下文中。这两种上下文中理论上不应该让当前的execution path进入sleep状态(虽然不是强制规定,换句话说,一个拥有spinlock的进程进入sleep并不必然意味着系统就一定会deadlock 等,但是对内核编程而言,还是应该尽力避开这个雷区)。
在CONFIG_DEBUG_ATOMIC_SLEEP选项打开的情形下,might_sleep又有哪些特殊的功能呢?先看看内核中的源码:
<kernel/sched.c>
点击(此处)折叠或打开
- void __might_sleep(const char *file, int line, int preempt_offset)
- {
- static unsigned long prev_jiffy; /* ratelimiting */
-
- if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) ||
- system_state != SYSTEM_RUNNING || oops_in_progress)
- return;
- if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
- return;
- prev_jiffy = jiffies;
-
- printk(KERN_ERR
- "BUG: sleeping function called from invalid context at %s:%d\n",
- file, line);
- printk(KERN_ERR
- "in_atomic(): %d, irqs_disabled(): %d, pid: %d, name: %s\n",
- in_atomic(), irqs_disabled(),
- current->pid, current->comm);
-
- if (irqs_disabled())
- print_irqtrace_events(current);
- dump_stack();
- }
<include/linux/kernel.h>
点击(此处)折叠或打开
- # define might_sleep() \
- do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)
所以让CONFIG_DEBUG_ATOMIC_SLEEP选项打开,可以捕捉到在一个atomic context中是否发生了sleep,如果你的代码不小心在某处的确出现了这种情形,那么might_sleep会通过后续的printk以及dump_stack来协助你发现这种情形。
至于__might_sleep函数中的system_state,它是一个全局性的enum型变量,主要用来记录当前系统的状态:
<init/main.c>
点击(此处)折叠或打开
- enum system_states system_state __read_mostly;
- EXPORT_SYMBOL(system_state);
<include/linux/kernel.h>
点击(此处)折叠或打开
- extern enum system_states {
- SYSTEM_BOOTING,
- SYSTEM_RUNNING,
- SYSTEM_HALT,
- SYSTEM_POWER_OFF,
- SYSTEM_RESTART,
- SYSTEM_SUSPEND_DISK,
- } system_state;