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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

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

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

一、引出

在在嵌入式操作系統(tǒng)中,很多線程都可以為實(shí)時(shí)任務(wù),因?yàn)楫吘惯@些線程很少和人接觸,而是面向任務(wù)的。所有就有一個(gè)搶占的時(shí)機(jī)問題。特別是2.6內(nèi)核中引入了新的內(nèi)核態(tài)搶占任務(wù),所以就可以說一下這個(gè)內(nèi)核態(tài)搶占的實(shí)現(xiàn)。

內(nèi)核態(tài)搶占主要發(fā)生在兩個(gè)時(shí)機(jī),一個(gè)是主動(dòng)的檢測(cè)是否需要搶占,另一個(gè)就是在異常處理完之后的異常判斷。

#define preempt_enable() \

do { \

preempt_enable_no_resched(); \

barrier(); \

preempt_check_resched(); \

} while (0)

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

另一個(gè)就是內(nèi)核線程無法預(yù)測(cè)的中斷或者異常處理結(jié)束之后的判斷。

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 ?首先判斷線程是否禁止了搶占,如果禁止搶占,則不檢測(cè)是否重新調(diào)度標(biāo)志。

jnz restore_nocheck

need_resched:

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

testb $_TIF_NEED_RESCHED, %cl檢測(cè)是否需要重新調(diào)度。

jz restore_all

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

jz restore_all

call preempt_schedule_irq?這里就是第二個(gè)搶占發(fā)生的時(shí)機(jī),就是內(nèi)核線程不可預(yù)測(cè)的時(shí)候發(fā)生的。

jmp need_resched

END(resume_kernel)

在preempt_schedule_irq中引入了一個(gè)比較常見的概念,就是這個(gè)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

這個(gè)標(biāo)志位在內(nèi)核中的線程搶占統(tǒng)計(jì)中將會(huì)用到,在schedule函數(shù)中

switch_count = &prev->nivcsw;

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

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可以看到這個(gè)切換次數(shù)記錄。

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);

}

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

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();

}

也就是,如果 線程是禁止搶占之后進(jìn)行調(diào)度,后果是很嚴(yán)重的,直接內(nèi)核就dump_stack了(但是沒有panic,至少對(duì)386是如此)。這一點(diǎn)也容易理解,因?yàn)樵谧栽负头亲栽傅膬蓚€(gè)搶占判斷中,都判斷了線程的preempt_count的值,如果非零就退出,所以應(yīng)該是不能走到這一步的。

二、多核中的運(yùn)行隊(duì)列

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

在內(nèi)核里,這個(gè)隊(duì)列就是每個(gè)CPU都定義的一個(gè)為struct rq 結(jié)構(gòu)的runqueue變量,這個(gè)是每個(gè)CPU的一個(gè)排隊(duì)區(qū),可以認(rèn)為是CPU的一個(gè)私有資源,并且是靜態(tài)分配,每個(gè)CPU有天生擁有這么一個(gè)隊(duì)列,拿人權(quán)的角度看,這個(gè)也就是CPU的一個(gè)基本權(quán)利,并且是一個(gè)內(nèi)置權(quán)利。當(dāng)CPU存在之后,它的runqueue就存在了。注意:這是一個(gè)容器,它是用來存放它的客戶線程的,所以的線程在這里進(jìn)行匯集和等待;對(duì)每個(gè)CPU來說,它的這個(gè)結(jié)構(gòu)本身是不會(huì)變化的,變化的只是這個(gè)隊(duì)列中的線程,一個(gè)線程可以在這個(gè)CPU隊(duì)列里等待并運(yùn)行,也可以在另一個(gè)CPU中運(yùn)行,當(dāng)然不能同時(shí)運(yùn)行。這個(gè)變量的定義為

static DEFINE_PER_CPU(struct rq, runqueues);

現(xiàn)在,一個(gè)CPU需要服務(wù)的所有的線程都在這個(gè)結(jié)構(gòu)里,所以也就包含了實(shí)時(shí)線程組和非實(shí)時(shí)線程組,它們?cè)趓q的體現(xiàn)為兩個(gè)成員。

struct cfs_rq cfs;

struct rt_rq rt;

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

對(duì)于cfs調(diào)度,它一般是為了保證系統(tǒng)中線程對(duì)用戶的及時(shí)響應(yīng),也就是說這個(gè)線程和用戶交互,不能讓用戶感覺到某個(gè)任務(wù)有“卡”的感覺。保證這個(gè)流暢的方法就是快速切換,從而在某個(gè)時(shí)間段內(nèi)所有的cfs任務(wù)都可以被運(yùn)行一次。也就是不會(huì)出現(xiàn)某個(gè)任務(wù)跑的很歡樂,另外某個(gè)跑的很苦逼。

這個(gè)的實(shí)現(xiàn)就是大家經(jīng)常說的內(nèi)核紅黑樹結(jié)構(gòu),很多地方都有說明。這里注意紅黑樹是一個(gè)有序樹,有序就需要有鍵值,并且有鍵值的比較方法。在內(nèi)核中這個(gè)鍵值就是每個(gè)線程的一個(gè)調(diào)度實(shí)體的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;

}

而這個(gè)調(diào)度實(shí)體是一個(gè)抽象的概念,它可能考慮到了任務(wù)組的調(diào)度吧。實(shí)時(shí)任務(wù)和cfs任務(wù)的調(diào)度實(shí)體結(jié)構(gòu)并不相同,并且這個(gè)兩個(gè)在task_struct結(jié)構(gòu)中兩個(gè)并不是一個(gè)union,而是實(shí)實(shí)在在兩個(gè)獨(dú)立的實(shí)體,在task_struct結(jié)構(gòu)中可以看到:

struct sched_entity se;

struct sched_rt_entity rt;

這個(gè)可能是為了保證線程的優(yōu)先級(jí)可以在運(yùn)行時(shí)通過sys_sched_setscheduler來動(dòng)態(tài)修改而設(shè)置的吧。

對(duì)于一個(gè)runqueue,它對(duì)應(yīng)一個(gè)CPU,由于一個(gè)CPU上只能同時(shí)運(yùn)行一個(gè)線程,所以一個(gè)runqueue只有一個(gè)curr,因?yàn)槲覀兛梢钥吹揭粋€(gè)rq有一個(gè)curr結(jié)構(gòu)

struct task_struct *curr, *idle, *stop;

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

我們看一下系統(tǒng)喚醒一個(gè)線程時(shí)的操作:

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

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

而對(duì)于某個(gè)CPU上正在運(yùn)行的線程的判斷則使用的是

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

{

return rq->curr == p;

}

而對(duì)于nr_running的設(shè)置為

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

static void inc_nr_running(struct rq *rq)

{

rq->nr_running++;

}

標(biāo)簽:task,搶占,rq,runqueue,線程,中多,CPU,struct

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。