might_sleep(): 指示當前函數可以睡眠。如果它所在的函數處於原子上下文(atomic context)中(如,spinlock, irq-handler…),將打印出堆棧的回溯信息。這個函數主要用來做調試工作,在你不確定不期望睡眠的地方是否真的不會睡眠時,就把這個宏加進去。
這個函數我在看代碼時基本上是直接忽略的(因爲我知道它實際上不幹什麼事),不過因爲內核中很多函數一開始就會用一下它,爲了方便那些正在學習內核源碼的網友,本帖專門討論一下該函數到底被內核用來幹什麼。
簡單地說,如果沒有調試的需求(絕大多數下你平常跑的系統都是release版本的kernel),那麼這個宏(或者函數,稱謂並不重要)什麼實質性的活都不幹,內核只是用它來做一件事,就是提醒你,調用該函數的函數可能會sleep,這個跟其名字也是匹配的: The function calling might_sleep() might sleep。如果你想看源碼,我把它列在下面:
點擊(此處)摺疊或打開
- # 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又有哪些特殊的功能呢?先看看內核中的源碼:
點擊(此處)摺疊或打開
- 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();
- }
點擊(此處)摺疊或打開
- # 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型變量,主要用來記錄當前系統的狀態:
點擊(此處)摺疊或打開
- enum system_states system_state __read_mostly;
- EXPORT_SYMBOL(system_state);
點擊(此處)摺疊或打開
- extern enum system_states {
- SYSTEM_BOOTING,
- SYSTEM_RUNNING,
- SYSTEM_HALT,
- SYSTEM_POWER_OFF,
- SYSTEM_RESTART,
- SYSTEM_SUSPEND_DISK,
- } system_state;