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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux runqueue定义,Linux中多CPU的runqueue及抢占

發布時間:2025/3/12 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux runqueue定义,Linux中多CPU的runqueue及抢占 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、引出

在在嵌入式操作系統中,很多線程都可以為實時任務,因為畢竟這些線程很少和人接觸,而是面向任務的。所有就有一個搶占的時機問題。特別是2.6內核中引入了新的內核態搶占任務,所以就可以說一下這個內核態搶占的實現。

內核態搶占主要發生在兩個時機,一個是主動的檢測是否需要搶占,另一個就是在異常處理完之后的異常判斷。

#define preempt_enable() \

do { \

preempt_enable_no_resched(); \

barrier(); \

preempt_check_resched(); \

} while (0)

這一點和用戶態的pthread_setcanceltype中也有使用,也就是如果禁止線程的異步取消,在使能之后的第一時間判斷線程是不是已經被取消,包括內核態對信號的處理也是如此,例如,當sigprocmask開啟一個信號屏蔽之后,也需要在第一時間來判斷系統中是否有未處理的信號,如果有則需要及時處理,這個操作是在sys_sigprocmask--->>>recalc_sigpending--->>>set_tsk_thread_flag(t, TIF_SIGPENDING)中完成。

另一個就是內核線程無法預測的中斷或者異常處理結束之后的判斷。

linux-2.6.21\arch\i386\kernel\entry.S:? ret_from_exception(ret_from_intr)--->>>>

#ifdef CONFIG_PREEMPT

ENTRY(resume_kernel)

DISABLE_INTERRUPTS(CLBR_ANY)

cmpl $0,TI_preempt_count(%ebp)?# non-zero preempt_count ?首先判斷線程是否禁止了搶占,如果禁止搶占,則不檢測是否重新調度標志。

jnz restore_nocheck

need_resched:

movl TI_flags(%ebp), %ecx?# need_resched set ?

testb $_TIF_NEED_RESCHED, %cl檢測是否需要重新調度。

jz restore_all

testl $IF_MASK,PT_EFLAGS(%esp)?# interrupts off (exception path) ?

jz restore_all

call preempt_schedule_irq?這里就是第二個搶占發生的時機,就是內核線程不可預測的時候發生的。

jmp need_resched

END(resume_kernel)

在preempt_schedule_irq中引入了一個比較常見的概念,就是這個PREEMPT_ACTIVE,

add_preempt_count(PREEMPT_ACTIVE);

/*

* We use bit 30 of the preempt_count toindicate that kernel

*?preemption is occurring.? See include/asm-arm/hardirq.h.

*/

#define PREEMPT_ACTIVE?0x40000000

這個標志位在內核中的線程搶占統計中將會用到,在schedule函數中

switch_count = &prev->nivcsw;

if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {這里判斷的是搶占標志是否被置位,如果沒有置位,也就是如果不是被搶占,則認為是自愿放棄CPU,也就是Voluntary釋放CPU,否則認為是被搶占。

switch_count = &prev->nvcsw;

if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&

unlikely(signal_pending(prev))))

prev->state = TASK_RUNNING;

else {

if (prev->state == TASK_UNINTERRUPTIBLE)

rq->nr_uninterruptible++;

deactivate_task(prev, rq);

}

}

通過/proc/$PID/status可以看到這個切換次數記錄。

static inline void task_context_switch_counts(struct seq_file *m,

struct task_struct *p)

{

seq_printf(m,?"voluntary_ctxt_switches:\t%lu\n"

"nonvoluntary_ctxt_switches:\t%lu\n",

p->nvcsw,

p->nivcsw);

}

從調度的代碼中可以看到,如果線程禁止了搶占,那么線程是不能執行調度的,這樣可以推出線程在關掉搶占之后不能睡眠,如果需要等待,應該應該用spinlock,在asmlinkage void __sched schedule(void)的開始有這個判斷

if (unlikely(in_atomic() && !current->exit_state)) {

printk(KERN_ERR "BUG: scheduling while atomic: "

"%s/0x%08x/%d\n",

current->comm, preempt_count(), current->pid);

debug_show_held_locks(current);

if (irqs_disabled())

print_irqtrace_events(current);

dump_stack();

}

也就是,如果 線程是禁止搶占之后進行調度,后果是很嚴重的,直接內核就dump_stack了(但是沒有panic,至少對386是如此)。這一點也容易理解,因為在自愿和非自愿的兩個搶占判斷中,都判斷了線程的preempt_count的值,如果非零就退出,所以應該是不能走到這一步的。

二、多核中的運行隊列

這個在大型服務器中是比較有用的一個概念,就是線程在CPU之間的均勻分配或者非均勻分配問題。目的就是讓各個CPU盡量負載平衡,不要忙的忙死,閑的閑死。按照計算機的原始概念,CPU可以作為一個資源,然后等待使用這個資源的線程就需要排隊。如果要排隊,就需要有一個約定的地點讓大家在這里排隊,這樣便于管理,比如說先來先服務,然后優先級的判斷等。

在內核里,這個隊列就是每個CPU都定義的一個為struct rq 結構的runqueue變量,這個是每個CPU的一個排隊區,可以認為是CPU的一個私有資源,并且是靜態分配,每個CPU有天生擁有這么一個隊列,拿人權的角度看,這個也就是CPU的一個基本權利,并且是一個內置權利。當CPU存在之后,它的runqueue就存在了。注意:這是一個容器,它是用來存放它的客戶線程的,所以的線程在這里進行匯集和等待;對每個CPU來說,它的這個結構本身是不會變化的,變化的只是這個隊列中的線程,一個線程可以在這個CPU隊列里等待并運行,也可以在另一個CPU中運行,當然不能同時運行。這個變量的定義為

static DEFINE_PER_CPU(struct rq, runqueues);

現在,一個CPU需要服務的所有的線程都在這個結構里,所以也就包含了實時線程組和非實時線程組,它們在rq的體現為兩個成員。

struct cfs_rq cfs;

struct rt_rq rt;

同一個CPU上的兩個運行隊列采用不同的調度策略,實時策略也就是內核中希望實現的O(1)調度器,所以它的內容中包含了100個實時隊列結構。這個結構也和信號相同,首先有一個位圖,表示這個優先級是否有可運行線程,然后有一個指針數組,指向各個優先級的就緒線程,前者用于快速判斷最高優先級隊列下表,后者用于真正取出該優先級的線程。

對于cfs調度,它一般是為了保證系統中線程對用戶的及時響應,也就是說這個線程和用戶交互,不能讓用戶感覺到某個任務有“卡”的感覺。保證這個流暢的方法就是快速切換,從而在某個時間段內所有的cfs任務都可以被運行一次。也就是不會出現某個任務跑的很歡樂,另外某個跑的很苦逼。

這個的實現就是大家經常說的內核紅黑樹結構,很多地方都有說明。這里注意紅黑樹是一個有序樹,有序就需要有鍵值,并且有鍵值的比較方法。在內核中這個鍵值就是每個線程的一個調度實體的vruntime成員,在linux-2.6.37.1\kernel\sched_fair.c中我們看到的鍵值比較為put_prev_task_fair--->>>put_prev_entity--->>>__enqueue_entity--->>>entity_key

static inline s64 entity_key(struct cfs_rq *cfs_rq, struct sched_entity *se)

{

return se->vruntime?- cfs_rq->min_vruntime;

}

而這個調度實體是一個抽象的概念,它可能考慮到了任務組的調度吧。實時任務和cfs任務的調度實體結構并不相同,并且這個兩個在task_struct結構中兩個并不是一個union,而是實實在在兩個獨立的實體,在task_struct結構中可以看到:

struct sched_entity se;

struct sched_rt_entity rt;

這個可能是為了保證線程的優先級可以在運行時通過sys_sched_setscheduler來動態修改而設置的吧。

對于一個runqueue,它對應一個CPU,由于一個CPU上只能同時運行一個線程,所以一個runqueue只有一個curr,因為我們可以看到一個rq有一個curr結構

struct task_struct *curr, *idle, *stop;

注意的是,同一時間真正使用CPU的線程只有一個,但是一個CPU上可以有多個線程都是處于就緒狀態,也就是running狀態,我們可以看到這個running在rq、rt_rq、cfs_rq中都有相應的成員(nr_running)。這里說的running并不是他們在運行,而是可運行,他們是用來進行CPU之間負載均衡的,和是否正在CPU上運行沒有直接關系。反過來,一個線程是否處于可運行狀態,是通過p->se.on_rq 來判斷的。

我們看一下系統喚醒一個線程時的操作:

wake_up_new_task--->>>activate_task--->>enqueue_task

p->se.on_rq = 1; 這里可以看到,實時任務也是用了task_struct中的struct sched_entity se;成員,所以可以認為這是一個線程固有的成員,而struct sched_rt_entity rt;是為rt線程專門另外設置的一個附加成員,它們不是互斥或者說可替代的,而是基礎和附加屬性的關系。

而對于某個CPU上正在運行的線程的判斷則使用的是

static inline int task_current(struct rq *rq, struct task_struct *p)

{

return rq->curr == p;

}

而對于nr_running的設置為

wake_up_new_task--->>>activate_task--->>inc_nr_running(rq);

static void inc_nr_running(struct rq *rq)

{

rq->nr_running++;

}

標簽:task,搶占,rq,runqueue,線程,中多,CPU,struct

來源: https://www.cnblogs.com/tsecer/p/10485824.html

總結

以上是生活随笔為你收集整理的linux runqueue定义,Linux中多CPU的runqueue及抢占的全部內容,希望文章能夠幫你解決所遇到的問題。

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