日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BUG: scheduling while atomic 分析 and 为什么中断不能睡眠

發布時間:2024/1/18 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BUG: scheduling while atomic 分析 and 为什么中断不能睡眠 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

遇到一個BUG: scheduling while atomic: kworker/0:2/370/0x00000002;看了這篇文章BUG: scheduling while atomic 分析,是因為在原子操作上下文或者中斷上下文進行了調度引起的。

先看下為什么會打印出這句:

schedule() -> __schedule() -> schedule_debug() static inline void schedule_debug(struct task_struct *prev) { #ifdef CONFIG_SCHED_STACK_END_CHECKif (task_stack_end_corrupted(prev))panic("corrupted stack end detected inside scheduler\n"); #endifif (unlikely(in_atomic_preempt_off())) {__schedule_bug(prev);preempt_count_set(PREEMPT_DISABLED);}rcu_sleep_check();profile_hit(SCHED_PROFILING, __builtin_return_address(0));schedstat_inc(this_rq()->sched_count); }/** Print scheduling while atomic bug:*/ static noinline void __schedule_bug(struct task_struct *prev) {/* Save this before calling printk(), since that will clobber it */unsigned long preempt_disable_ip = get_preempt_disable_ip(current);if (oops_in_progress)return;printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n",prev->comm, prev->pid, preempt_count());debug_show_held_locks(prev);print_modules();if (irqs_disabled())print_irqtrace_events(prev);if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)&& in_atomic_preempt_off()) {pr_err("Preemption disabled at:");print_ip_sym(preempt_disable_ip);pr_cont("\n");}if (panic_on_warn)panic("scheduling while atomic\n");dump_stack();add_taint(TAINT_WARN, LOCKDEP_STILL_OK); }

也就是 if (unlikely(in_atomic_preempt_off())) 這句成立,就會報這個錯誤。

#define in_atomic_preempt_off() (preempt_count() != PREEMPT_DISABLE_OFFSET)

preempt_count()? 讀取 preempt_count,這個成員被用來判斷當前進程是否可以被搶占,這個值是一個 task 的 thread info 中的一個成員變量。也就是 preempt_count 被改變不等于 PREEMPT_DISABLE_OFFSET 之后就會報這個錯。可能是代碼調用 preempt_disable 顯式的禁止了搶占,也可能是處于中斷上下文等。其中 preempt_disable 和 preempt_enable 成對出現是對 preempt_count 進行加一和減一的操作。

那么 preempt_count 在什么情況下會發生改變呢?

1.原子操作上下文中,比如spin_lock。

spin_lock()->raw_spin_lock()->_raw_spin_lock()->__raw_spin_lock()static inline void __raw_spin_lock(raw_spinlock_t *lock) {preempt_disable();spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); }

2.中斷上下文中。

handle_IRQ()->__handle_domain_irq()->irq_enter()->__irq_enter()#define __irq_enter() \do { \account_irq_enter_time(current); \preempt_count_add(HARDIRQ_OFFSET); \trace_hardirq_enter(); \} while (0)

硬件中斷來的時候會調用 handle_IRQ 進中斷處理函數,會調用到 preempt_count_add(HARDIRQ_OFFSET),這個函數雖然不是像 preempt_disable 將 preempt_count 加一,但是它過分的是將其加 HARDIRQ_OFFSET(1UL << 16),這個時候顯然是不等于 PREEMPT_DISABLE_OFFSET,如果調用 schedule 就會報 bug。

這也同時讓我想起之前說為什么中斷不能睡眠,網上多數分析的原因都是些主觀上的不能睡眠調度的原因,并沒有從代碼上分析。其中讓我夜不能寐一句說:

2.4內核中schedule()函數本身在進來的時候判斷是否處于中斷上下文:

if(unlikely(in_interrupt()))

BUG();

因此,強行調用schedule()的結果就是內核BUG,但我看2.6.18的內核schedule()的實現卻沒有這句,改掉了。

其實2.6以上是有實現的,就是上面的 flow 觸發。2.4是分別判斷 in_atomic 和 in_interrupt (咱也沒找到2.4的kernel code,咱就敢說,你找去吧,反正你也找不到),2.6以后將所有不允許調度的情況都集合在 preempt_count 這個變量上,用不同的位來代表不同的情況,具體preempt_count的數據格式可以參考下圖:

preemption count 用來記錄當前被顯式的禁止搶占的次數,這就和代碼里一致了:中斷中是加 HARDIRQ_OFFSET(1UL << 16) 對應 bit16,原子上下文是加1對應 bit0。

我們再看那句log:

printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n",prev->comm, prev->pid, preempt_count());

最后會將?preemption count 打印出來,這樣就能通過這個值來看是因為在哪種情況下調用 schedule 而導致的 kernel crash,還是內核牛批啊。

所以說也不能老是聽大佬說中斷不能調睡眠函數又不去想為啥,還是得看代碼怎么實現的,它萬一有一天客戶拿槍頂著我的腦袋讓我解釋個中原因呢?

?我賭你槍里沒有子彈!

?

參考文章:

中斷上下文中調度會怎樣?

linux kernel的中斷子系統之(八):softirq

總結

以上是生活随笔為你收集整理的BUG: scheduling while atomic 分析 and 为什么中断不能睡眠的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。