小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

網(wǎng)易博客歡迎您

 老匹夫 2014-09-12

        unsigned long nr_uninterruptible;

下面幾個(gè)指針分別指向當(dāng)前運(yùn)行的進(jìn)程、空閑進(jìn)程、被停止運(yùn)行的進(jìn)程

        struct task_struct *curr, *idle, *stop;

        unsigned long next_balance;

        struct mm_struct *prev_mm;進(jìn)程切換時(shí)存放被替換進(jìn)程內(nèi)存描述符

字段clock用于實(shí)現(xiàn)就緒隊(duì)列自身的時(shí)鐘,每次調(diào)用周期性調(diào)度器時(shí),都會(huì)更新clock的值。內(nèi)核還提供了函數(shù)update_rq_clock,可在操作就緒隊(duì)列的調(diào)度中多次調(diào)用。

        u64 clock; 

......

};

調(diào)度類結(jié)構(gòu)體在文件include/linux/sched.h中定義,如下:

struct sched_class {

next字段將各個(gè)調(diào)度類按重要性鏈接起來,最重要的是實(shí)時(shí)進(jìn)程,其次是完全公平進(jìn)程,再次是空閑進(jìn)程

        const struct sched_class *next;

向就緒隊(duì)列添加一個(gè)新進(jìn)程,在進(jìn)程從睡眠狀態(tài)變?yōu)榭蛇\(yùn)行狀態(tài)調(diào)用該操作

        void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);

將一個(gè)進(jìn)程從就緒隊(duì)列中去除。

        void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);

當(dāng)進(jìn)程志愿放棄對(duì)處理器的控制權(quán)時(shí),可使用系統(tǒng)調(diào)用sched_yield,這將導(dǎo)致內(nèi)核調(diào)用

函數(shù)yield_task

        void (*yield_task) (struct rq *rq);

進(jìn)程放棄CPU控制權(quán)并將控制權(quán)交給p進(jìn)程

        bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt);

用一個(gè)新喚醒的進(jìn)程來搶占當(dāng)前進(jìn)程,

        void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags);

選擇下一個(gè)需要運(yùn)行的進(jìn)程

        struct task_struct * (*pick_next_task) (struct rq *rq);

通知調(diào)度器類當(dāng)前運(yùn)行的進(jìn)程將要被另一個(gè)進(jìn)程代替。

        void (*put_prev_task) (struct rq *rq, struct task_struct *p);

......

用于設(shè)置調(diào)度隊(duì)列的當(dāng)前進(jìn)程,例如cfs_rq->curr

        void (*set_curr_task) (struct rq *rq);

由周期性調(diào)度器調(diào)用,更新一些時(shí)間統(tǒng)計(jì)量

        void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);

當(dāng)新進(jìn)程創(chuàng)建時(shí)調(diào)用初始化一些特定調(diào)度器類的東西,并設(shè)置重調(diào)度標(biāo)志TIF_NEED_RESCHED

        void (*task_fork) (struct task_struct *p);

......

};

調(diào)度器操作比進(jìn)程更一般的實(shí)體,每個(gè)進(jìn)程描述結(jié)構(gòu)中都嵌有一個(gè)調(diào)度實(shí)體結(jié)構(gòu),但并不是一個(gè)調(diào)度實(shí)體就等于一個(gè)進(jìn)程,有時(shí)候?qū)⒁唤M進(jìn)程集合起來表示一個(gè)調(diào)度實(shí)體。調(diào)度實(shí)體結(jié)構(gòu)在文件include/linux/sched.h中定義,如下:

struct sched_entity {

load記錄一個(gè)調(diào)度實(shí)體負(fù)荷權(quán)重

        struct load_weight      load;           /* for load-balancing */

紅黑樹節(jié)點(diǎn),使得實(shí)體可以在紅黑樹上排序

        struct rb_node          run_node;

用于鏈接到就緒隊(duì)列的cfs_tasks字段上

        struct list_head        group_node;

表示實(shí)體是否在就緒隊(duì)列上

        unsigned int            on_rq;

當(dāng)新進(jìn)程被加入就緒隊(duì)列或者在周期性調(diào)度器中,會(huì)計(jì)算當(dāng)前值和exec_start之間的差值,將差值加到sum_exec_runtime中,exec_start則更新為當(dāng)前時(shí)間

        u64                     exec_start;

記錄消耗的CPU時(shí)間

        u64                     sum_exec_runtime;

保存進(jìn)程的虛擬時(shí)間,關(guān)于虛擬時(shí)間后面講詳細(xì)講解

        u64                     vruntime;

當(dāng)進(jìn)程失去CPU控制權(quán)時(shí)將sum_exec_runtime保存到prev_sum_exec_runtime中

        u64                     prev_sum_exec_runtime;

......

下面是跟組調(diào)度相關(guān)的字段

#ifdef CONFIG_FAIR_GROUP_SCHED

        struct sched_entity     *parent; 建立組調(diào)度中調(diào)度實(shí)體的層次關(guān)系

        struct cfs_rq           *cfs_rq; 組所屬的調(diào)度隊(duì)列

        /* rq "owned" by this entity/group: */

        struct cfs_rq           *my_q;組內(nèi)部的調(diào)度隊(duì)列

#endif

};

2 優(yōu)先級(jí)與負(fù)荷權(quán)重2.1優(yōu)先級(jí)的計(jì)算

前面講過優(yōu)先級(jí)有3個(gè),static_prio、normal_prio和prio。Static_prio在進(jìn)程啟動(dòng)時(shí)靜態(tài)設(shè)置可用nice系統(tǒng)調(diào)用修改;normal_prio與靜態(tài)優(yōu)先級(jí)和調(diào)度策略有關(guān),調(diào)度策略可能被修改;prio是調(diào)度算法中用到的優(yōu)先級(jí),他可能被內(nèi)核臨時(shí)改變,比如一個(gè)高優(yōu)先級(jí)的進(jìn)程在使用的互斥量,被一個(gè)低優(yōu)先級(jí)進(jìn)程占據(jù)了,這是就要臨時(shí)提高低優(yōu)先級(jí)的優(yōu)先級(jí),這個(gè)優(yōu)先級(jí)就保存在prio中。

在用戶空間中,用nice系統(tǒng)調(diào)用設(shè)置進(jìn)程的靜態(tài)優(yōu)先級(jí),該nice值的范圍是-20到19之間,值越低優(yōu)先級(jí)越高。內(nèi)核內(nèi)部表示優(yōu)先級(jí)的的范圍是0到139,同樣也是值越低優(yōu)先級(jí)越高。從0到99專事實(shí)進(jìn)程使用。Nice值-20到19映射的范圍是100到139.

優(yōu)先級(jí)prio的獲取是通過函數(shù)effective_prio來實(shí)現(xiàn)的,如下:

程序首先計(jì)算進(jìn)程的普通優(yōu)先級(jí)然后判斷進(jìn)程的優(yōu)先級(jí)是否小于MAX_RT_PRIO,這里并不考慮調(diào)度策略。如果進(jìn)程優(yōu)先級(jí)在實(shí)時(shí)進(jìn)程優(yōu)先級(jí)范圍內(nèi)就直接返回其優(yōu)先級(jí)(prio)否則返回其普通優(yōu)先級(jí)。

static int effective_prio(struct task_struct *p)

{

        p->normal_prio = normal_prio(p);

        if (!rt_prio(p->prio))

                return p->normal_prio;

        return p->prio;

}

【effective_prio--->normal_prio】

由于實(shí)時(shí)進(jìn)程中,進(jìn)程描述符p->rt_priority字段表示的優(yōu)先級(jí)是其數(shù)值越大優(yōu)先級(jí)越高,與內(nèi)核內(nèi)部表示優(yōu)先級(jí)的方法剛好相反,所以在內(nèi)核內(nèi)部優(yōu)先級(jí)的計(jì)算方法是 MAX_RT_PRIO-1 - p->rt_priority;。函數(shù)__normal_prio僅僅是返回進(jìn)程的靜態(tài)優(yōu)先級(jí) return p->static_prio;。

static inline int normal_prio(struct task_struct *p)

{

        int prio;

        if (task_has_rt_policy(p))

                prio = MAX_RT_PRIO-1 - p->rt_priority;

        else

                prio = __normal_prio(p);

        return prio;

}

2.2 負(fù)荷權(quán)重的計(jì)算

進(jìn)程每降低一個(gè)nice值,多獲得10%的CPU時(shí)間,每升高一個(gè)nice值,則放棄10%的CPU時(shí)間。為執(zhí)行該策略,內(nèi)核將優(yōu)先級(jí)轉(zhuǎn)換為權(quán)重。優(yōu)先級(jí)--權(quán)重轉(zhuǎn)換表:

kernel/sched/sched.h

static const int prio_to_weight[40] = {

 /* -20 */     88761,     71755,     56483,     46273,     36291,

 /* -15 */     29154,     23254,     18705,     14949,     11916,

 /* -10 */      9548,      7620,      6100,      4904,      3906,

 /*  -5 */      3121,      2501,      1991,      1586,      1277,

 /*   0 */      1024,       820,       655,       526,       423,

 /*   5 */       335,       272,       215,       172,       137,

 /*  10 */       110,        87,        70,        56,        45,

 /*  15 */        36,        29,        23,        18,        15,

};

計(jì)算權(quán)重的函數(shù)是set_load_weight,空閑進(jìn)程的權(quán)重最小。負(fù)荷權(quán)重包含在數(shù)據(jù)結(jié)構(gòu)load_weight中如下:

struct load_weight { 

        unsigned long weight, inv_weight;

};

Weight表示優(yōu)先級(jí)在表prio_to_weight中映射的權(quán)重,inv_weight表示被負(fù)荷權(quán)重除的結(jié)果,這些值也存在表prio_to_wmult中,這個(gè)表與表prio_to_weight中的值一一對(duì)應(yīng)。這些值的計(jì)算方法是(2^32/x),x表示在表prio_to_weight中對(duì)應(yīng)位置中的值。

static void set_load_weight(struct task_struct *p)

{

        int prio = p->static_prio - MAX_RT_PRIO;

        struct load_weight *load = &p->se.load;

        if (p->policy == SCHED_IDLE) {

                load->weight = scale_load(WEIGHT_IDLEPRIO);

                load->inv_weight = WMULT_IDLEPRIO;

                return;

        }

 

        load->weight = scale_load(prio_to_weight[prio]);   

        load->inv_weight = prio_to_wmult[prio];  

}

3核心調(diào)度器

調(diào)度器的實(shí)現(xiàn)基于兩個(gè)函數(shù):周期性調(diào)度器函數(shù)和主調(diào)度器函數(shù)。

周期性調(diào)度器函數(shù)scheduler_tick:在內(nèi)核頻率HZ中斷處理函數(shù)中調(diào)用該函數(shù)。周期性調(diào)度函數(shù)的主要工作是更新各種時(shí)間統(tǒng)計(jì)量,在必要的時(shí)候進(jìn)行進(jìn)程 調(diào)度。在多CPU系統(tǒng)中它還進(jìn)行負(fù)載均衡處理。

主調(diào)度器函數(shù)schedule:如果進(jìn)程主動(dòng)放棄執(zhí)行而選擇另一個(gè)活動(dòng)進(jìn)程運(yùn)行就調(diào)用該函數(shù)。

周期性調(diào)度器函數(shù)和主調(diào)度器函數(shù)都不直接操作進(jìn)程,而是調(diào)用進(jìn)程所屬的操作類來操作進(jìn)程。每個(gè)進(jìn)程都屬于某個(gè)調(diào)度類(完全公平調(diào)度類、實(shí)時(shí)調(diào)度類)。

3.1 周期性調(diào)度器

kernel/sched/core.c

void scheduler_tick(void)

{

        int cpu = smp_processor_id(); 獲取當(dāng)前CPU的cpu id

        struct rq *rq = cpu_rq(cpu);獲取當(dāng)前CPU對(duì)應(yīng)的就緒隊(duì)列結(jié)構(gòu)

        struct task_struct *curr = rq->curr;獲取當(dāng)前進(jìn)程的進(jìn)程描述符

在函數(shù)update_rq_clock中,更新就緒隊(duì)列的兩個(gè)成員rq->clock_task,rq->clock。

        update_rq_clock(rq);

函數(shù)update_cpu_load_active負(fù)責(zé)更新就緒隊(duì)列的cpu_load[]數(shù)組。將數(shù)組中先前存儲(chǔ)的負(fù)荷值向后移動(dòng)一個(gè)位置,將當(dāng)前就緒隊(duì)列的負(fù)荷計(jì)入數(shù)組的第一個(gè)位置。

        update_cpu_load_active(rq);

調(diào)用當(dāng)前進(jìn)程所屬的調(diào)度類的task_tick函數(shù),在該函數(shù)中更新一些時(shí)間計(jì)數(shù),必要時(shí)調(diào)度其他活動(dòng)進(jìn)程來執(zhí)行。

        curr->sched_class->task_tick(rq, curr, 0);

......

#ifdef CONFIG_SMP

        rq->idle_balance = idle_cpu(cpu);

在多處理器系統(tǒng)中進(jìn)行負(fù)載均衡,保證不同CPU的運(yùn)行隊(duì)列包含數(shù)量基本相同的可運(yùn)行進(jìn)程

        trigger_load_balance(rq, cpu);

#endif

}

3.2 主調(diào)度器

kernel/sched/core.c

asmlinkage void __sched schedule(void)

{

        struct task_struct *tsk = current;

提交一些阻塞的IO請(qǐng)求避免死鎖。

        sched_submit_work(tsk);

主調(diào)度器的組要工作都在函數(shù)__schedule中實(shí)現(xiàn)。

        __schedule();

}     

【schedule--->__schedule】

static void __sched __schedule(void)

{

        struct task_struct *prev, *next;

        unsigned long *switch_count;

        struct rq *rq;

        int cpu;

need_resched:

        preempt_disable(); 禁止搶占

        cpu = smp_processor_id(); 獲取當(dāng)前進(jìn)程CPU id

        rq = cpu_rq(cpu);獲取當(dāng)前cpu對(duì)應(yīng)的就緒隊(duì)列結(jié)構(gòu)

因?yàn)橐袚Q到其他活動(dòng)進(jìn)程,所以當(dāng)前進(jìn)程就變成了“以前的”進(jìn)程了。

        prev = rq->curr;

......

保存進(jìn)程切換計(jì)數(shù)

        switch_count = &prev->nivcsw;

標(biāo)志 PREEMPT_ACTIVE可以保證進(jìn)程被搶占但是不被移除就緒隊(duì)列

        if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {

如果進(jìn)程有非阻塞掛起信號(hào),而且狀態(tài)為TASK_INTERRUPTIBLE,就不將進(jìn)程移除就緒隊(duì)列,并將其狀態(tài)設(shè)為TASK_RUNNING。

                if (unlikely(signal_pending_state(prev->state, prev))) {

                        prev->state = TASK_RUNNING;

                } else {

調(diào)用函數(shù) p->sched_class->dequeue_task將進(jìn)程移除隊(duì)列。

                        deactivate_task(rq, prev, DEQUEUE_SLEEP);

                        prev->on_rq = 0;

                        if (prev->flags & PF_WQ_WORKER) {

                                struct task_struct *to_wakeup;

每一個(gè)工作隊(duì)列都有一個(gè)工作者線程,如果當(dāng)前進(jìn)程是一個(gè)工作者線程,就查看是否有其他處于等待中的工作者線程,如果有在本地CPU上喚醒。

                                to_wakeup = wq_worker_sleeping(prev, cpu);

                                if (to_wakeup)

                                        try_to_wake_up_local(to_wakeup);

                        }

                }

                switch_count = &prev->nvcsw;

        }

如果CPU將變?yōu)榭臻e狀態(tài),即就緒隊(duì)列上沒有可運(yùn)行的進(jìn)程,就試圖從其他CPU上可運(yùn)行的進(jìn)程移動(dòng)到本地CPU的就緒隊(duì)列上。

        if (unlikely(!rq->nr_running))

                idle_balance(cpu, rq);

通知調(diào)度器類當(dāng)前運(yùn)行的進(jìn)程將要被另一個(gè)進(jìn)程代替。

        put_prev_task(rq, prev);

選擇下一個(gè)應(yīng)該執(zhí)行的進(jìn)程

        next = pick_next_task(rq);

清除重調(diào)度標(biāo)志TIF_NEED_RESCHED,重調(diào)度標(biāo)志是由進(jìn)程調(diào)度類設(shè)置的,表示調(diào)度請(qǐng)求,內(nèi)核會(huì)在適當(dāng)?shù)臅r(shí)機(jī)完成該標(biāo)志。

        clear_tsk_need_resched(prev);

        rq->skip_clock_update = 0;

        if (likely(prev != next)) {

                rq->nr_switches++;

                rq->curr = next;

                ++*switch_count; 遞增進(jìn)程切換計(jì)數(shù)

完成進(jìn)程上下文切換,下面詳細(xì)分析。

                context_switch(rq, prev, next); /* unlocks the rq */

進(jìn)程已經(jīng)切換到其他進(jìn)程去運(yùn)行了,在后來的某個(gè)時(shí)候當(dāng)前進(jìn)程再次被調(diào)度時(shí)進(jìn)程從這里開始運(yùn)行,但是它在這中間可能已經(jīng)被移動(dòng)到其他CPU上了,所以此時(shí)棧上的cpu和rq變量的只可能就不一樣了。

                cpu = smp_processor_id();

                rq = cpu_rq(cpu);

        } else

                raw_spin_unlock_irq(&rq->lock);

        post_schedule(rq);

        sched_preempt_enable_no_resched();

        if (need_resched())

                goto need_resched;

}

【schedule--->__schedule--->context_switch】

static inline void context_switch(struct rq *rq, struct task_struct *prev,

               struct task_struct *next)

{

        struct mm_struct *mm, *oldmm;

        mm = next->mm;

        oldmm = prev->active_mm;

如果將要運(yùn)行的進(jìn)程是一個(gè)內(nèi)核線程,則它是沒有進(jìn)程虛擬地址空間的,就將上一個(gè)進(jìn)程的虛擬地址空間報(bào)保存到 next->active_mm 中,

        if (!mm) {

                next->active_mm = oldmm;

                atomic_inc(&oldmm->mm_count);

                enter_lazy_tlb(oldmm, next);

        } else

                switch_mm(oldmm, mm, next); 更換虛擬內(nèi)存上下文

如果前一個(gè)進(jìn)程是內(nèi)核線程就將前一個(gè)進(jìn)程的prev->active_mm重置為NULL,rq->prev_mm指向上一個(gè)進(jìn)程的虛擬地址空間

        if (!prev->mm) {

                prev->active_mm = NULL;

                rq->prev_mm = oldmm;

        }

......

進(jìn)程寄存器和棧的切換,有特定體系架構(gòu)的匯編實(shí)現(xiàn)。switch_to之后進(jìn)程已經(jīng)運(yùn)行在了新進(jìn)程之上,當(dāng)前進(jìn)程再次被調(diào)度運(yùn)行時(shí)這中間可能已經(jīng)經(jīng)歷過很多次切換了,但是要保證prev指向上一個(gè)進(jìn)程。這上一個(gè)進(jìn)程可能并不是當(dāng)前進(jìn)程,所以要傳遞三個(gè)參數(shù),下面代碼等效于prev=switch_to(next, prev);

        switch_to(prev, next, prev);

        barrier();

      

        finish_task_switch(this_rq(), prev);

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多