国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
linux調(diào)度器源碼分析3/4--新進(jìn)程加入
http://blog.chinaunix.net/uid-26772321-id-4888185.html
2015

引言

  之前的文章已經(jīng)介紹了調(diào)度器已經(jīng)初始化完成,現(xiàn)在只需要加入一個(gè)周期定時(shí)器tick驅(qū)動(dòng)它進(jìn)行周期調(diào)度即可,而加入定時(shí)器tick在下一篇文章進(jìn)行簡(jiǎn)單說明(主要這部分涉及調(diào)度器比較少,更多的是時(shí)鐘、定時(shí)器相關(guān)知識(shí))。這篇文章主要說明系統(tǒng)如何把一個(gè)進(jìn)程加入到隊(duì)列中。

 

加入時(shí)機(jī)

  之前的文章也有提到過,只有處于TASK_RUNNING狀態(tài)下的進(jìn)程才能夠加入到調(diào)度器,其他狀態(tài)都不行,也就說明了,當(dāng)一個(gè)進(jìn)程處于睡眠、掛起狀態(tài)的時(shí)候是不存在于調(diào)度器中的,而進(jìn)程加入調(diào)度器的時(shí)機(jī)如下:

  • 當(dāng)進(jìn)程創(chuàng)建完成時(shí),進(jìn)程剛創(chuàng)建完成時(shí),即使它運(yùn)行起來立即調(diào)用sleep()進(jìn)程睡眠,它也必定先會(huì)加入到調(diào)度器,因?yàn)閷?shí)際上它加入調(diào)度器后自己還需要進(jìn)行一定的初始化和操作,才會(huì)調(diào)用到我們的“立即”sleep()。
  • 當(dāng)進(jìn)程被喚醒時(shí),也使用sleep的例子說明,我們平常寫程序使用的sleep()函數(shù)實(shí)現(xiàn)原理就是通過系統(tǒng)調(diào)用將進(jìn)程狀態(tài)改為TASK_INTERRUPTIBLE,然后移出運(yùn)行隊(duì)列,并且啟動(dòng)一個(gè)定時(shí)器,在定時(shí)器到期后喚醒進(jìn)程,再重新放入運(yùn)行隊(duì)列。

sched_fork

  在我的博文關(guān)于linux系統(tǒng)如何實(shí)現(xiàn)fork的研究(二)中專門描述了copy_process()這個(gè)創(chuàng)建函數(shù),而里面有一個(gè)函數(shù)專門用于進(jìn)程調(diào)度的初始化,就是sched_fork(),其代碼如下

  1. int sched_fork(unsigned long clone_flags, struct task_struct *p)
  2. {
  3.     unsigned long flags;
  4.     /* 獲取當(dāng)前CPU,并且禁止搶占 */
  5.     int cpu = get_cpu();
  6.     
  7.     /* 初始化跟調(diào)度相關(guān)的值,比如調(diào)度實(shí)體,運(yùn)行時(shí)間等 */
  8.     __sched_fork(clone_flags, p);
  9.     /*
  10.      * 標(biāo)記為運(yùn)行狀態(tài),表明此進(jìn)程正在運(yùn)行或準(zhǔn)備好運(yùn)行,實(shí)際上沒有真正在CPU上運(yùn)行,這里只是導(dǎo)致了外部信號(hào)和事件不能夠喚醒此進(jìn)程,之后將它插入到運(yùn)行隊(duì)列中
  11.      */
  12.     p->state = TASK_RUNNING;

  13.     /*
  14.      * 根據(jù)父進(jìn)程的運(yùn)行優(yōu)先級(jí)設(shè)置設(shè)置進(jìn)程的優(yōu)先級(jí)
  15.      */
  16.     p->prio = current->normal_prio;

  17.     /*
  18.      * 更新該進(jìn)程優(yōu)先級(jí)
  19.      */
  20.     /* 如果需要重新設(shè)置優(yōu)先級(jí) */
  21.     if (unlikely(p->sched_reset_on_fork)) {
  22.         /* 如果是dl調(diào)度或者實(shí)時(shí)調(diào)度 */
  23.         if (task_has_dl_policy(p) || task_has_rt_policy(p)) {
  24.             /* 調(diào)度策略為SCHED_NORMAL,這個(gè)選項(xiàng)將使用CFS調(diào)度 */
  25.             p->policy = SCHED_NORMAL;
  26.             /* 根據(jù)默認(rèn)nice值設(shè)置靜態(tài)優(yōu)先級(jí) */
  27.             p->static_prio = NICE_TO_PRIO(0);
  28.             /* 實(shí)時(shí)優(yōu)先級(jí)為0 */
  29.             p->rt_priority = 0;
  30.         } else if (PRIO_TO_NICE(p->static_prio) < 0)
  31.             /* 根據(jù)默認(rèn)nice值設(shè)置靜態(tài)優(yōu)先級(jí) */
  32.             p->static_prio = NICE_TO_PRIO(0);

  33.         /* p->prio = p->normal_prio = p->static_prio */
  34.         p->prio = p->normal_prio = __normal_prio(p);
  35.         /* 設(shè)置進(jìn)程權(quán)重 */
  36.         set_load_weight(p);

  37.          /* sched_reset_on_fork成員在之后已經(jīng)不需要使用了,直接設(shè)為0 */
  38.         p->sched_reset_on_fork = 0;
  39.     }

  40.     if (dl_prio(p->prio)) {
  41.         /* 使能搶占 */
  42.         put_cpu();
  43.         /* 返回錯(cuò)誤 */
  44.         return -EAGAIN;
  45.     } else if (rt_prio(p->prio)) {
  46.         /* 根據(jù)優(yōu)先級(jí)判斷,如果是實(shí)時(shí)進(jìn)程,設(shè)置其調(diào)度類為rt_sched_class */
  47.         p->sched_class = &rt_sched_class;
  48.     } else {
  49.         /* 如果是普通進(jìn)程,設(shè)置其調(diào)度類為fair_sched_class */
  50.         p->sched_class = &fair_sched_class;
  51.     }
  52.     /* 調(diào)用調(diào)用類的task_fork函數(shù) */
  53.     if (p->sched_class->task_fork)
  54.         p->sched_class->task_fork(p);

  55.     /*
  56.      * The child is not yet in the pid-hash so no cgroup attach races,
  57.      * and the cgroup is pinned to this child due to cgroup_fork()
  58.      * is ran before sched_fork().
  59.      *
  60.      * Silence PROVE_RCU.
  61.      */
  62.     raw_spin_lock_irqsave(&p->pi_lock, flags);
  63.     /* 設(shè)置新進(jìn)程的CPU為當(dāng)前CPU */
  64.     set_task_cpu(p, cpu);
  65.     raw_spin_unlock_irqrestore(&p->pi_lock, flags);

  66. #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
  67.     if (likely(sched_info_on()))
  68.         memset(&p->sched_info, 0, sizeof(p->sched_info));
  69. #endif
  70. #if defined(CONFIG_SMP)
  71.     p->on_cpu = 0;
  72. #endif
  73.     /* task_thread_info(p)->preempt_count = PREEMPT_DISABLED; */
  74.     /* 初始化該進(jìn)程為內(nèi)核禁止搶占 */
  75.     init_task_preempt_count(p);
  76. #ifdef CONFIG_SMP
  77.     plist_node_init(&p->pushable_tasks, MAX_PRIO);
  78.     RB_CLEAR_NODE(&p->pushable_dl_tasks);
  79. #endif
  80.     /* 使能搶占 */
  81.     put_cpu();
  82.     return 0;
  83. }

   在sched_fork()函數(shù)中,主要工作如下:

  • 獲取當(dāng)前CPU號(hào)
  • 禁止內(nèi)核搶占(這里基本就是關(guān)閉了搶占,因?yàn)閳?zhí)行到這里已經(jīng)是內(nèi)核態(tài),又禁止了被搶占)
  • 初始化進(jìn)程p的一些變量(實(shí)時(shí)進(jìn)程和普通進(jìn)程通用的那些變量)
  • 設(shè)置進(jìn)程p的狀態(tài)為TASK_RUNNING(這一步很關(guān)鍵,因?yàn)橹挥刑幱赥ASK_RUNNING狀態(tài)下的進(jìn)程才會(huì)被調(diào)度器放入隊(duì)列中)
  • 根據(jù)父進(jìn)程和clone_flags參數(shù)設(shè)置進(jìn)程p的優(yōu)先級(jí)和權(quán)重。
  • 根據(jù)進(jìn)程p的優(yōu)先級(jí)設(shè)置其調(diào)度類(實(shí)時(shí)進(jìn)程優(yōu)先級(jí):0~99  普通進(jìn)程優(yōu)先級(jí):100~139)
  • 根據(jù)調(diào)度類進(jìn)行進(jìn)程p類型相關(guān)的初始化(這里就實(shí)現(xiàn)了實(shí)時(shí)進(jìn)程和普通進(jìn)程獨(dú)有的變量進(jìn)行初始化)
  • 設(shè)置進(jìn)程p的當(dāng)前CPU為此CPU。
  • 初始化進(jìn)程p禁止內(nèi)核搶占(因?yàn)楫?dāng)CPU執(zhí)行到進(jìn)程p時(shí),進(jìn)程p還需要進(jìn)行一些初始化)
  • 使能內(nèi)核搶占

  可以看出sched_fork()進(jìn)行的初始化也比較簡(jiǎn)單,需要注意的是不同類型的進(jìn)程會(huì)使用不同的調(diào)度類,并且也會(huì)調(diào)用調(diào)度類中的初始化函數(shù)。在實(shí)時(shí)進(jìn)程的調(diào)度類中是沒有特定的task_fork()函數(shù)的,而普通進(jìn)程使用cfs策略時(shí)會(huì)調(diào)用到task_fork_fair()函數(shù),我們具體看看實(shí)現(xiàn):

  1. static void task_fork_fair(struct task_struct *p)
  2. {
  3.     struct cfs_rq *cfs_rq;
  4.     
  5.     /* 進(jìn)程p的調(diào)度實(shí)體se */
  6.     struct sched_entity *se = &p->se, *curr;
  7.     
  8.     /* 獲取當(dāng)前CPU */
  9.     int this_cpu = smp_processor_id();
  10.     
  11.     /* 獲取此CPU的運(yùn)行隊(duì)列 */
  12.     struct rq *rq = this_rq();
  13.     unsigned long flags;
  14.     
  15.     /* 上鎖并保存中斷記錄 */
  16.     raw_spin_lock_irqsave(&rq->lock, flags);
  17.     
  18.     /* 更新rq運(yùn)行時(shí)間 */
  19.     update_rq_clock(rq);
  20.     
  21.     /* cfs_rq = current->se.cfs_rq; */
  22.     cfs_rq = task_cfs_rq(current);
  23.     
  24.     /* 設(shè)置當(dāng)前進(jìn)程所在隊(duì)列為父進(jìn)程所在隊(duì)列 */
  25.     curr = cfs_rq->curr;

  26.     /*
  27.      * Not only the cpu but also the task_group of the parent might have
  28.      * been changed after parent->se.parent,cfs_rq were copied to
  29.      * child->se.parent,cfs_rq. So call __set_task_cpu() to make those
  30.      * of child point to valid ones.
  31.      */
  32.     rcu_read_lock();
  33.     /* 設(shè)置此進(jìn)程所屬CPU */
  34.     __set_task_cpu(p, this_cpu);
  35.     rcu_read_unlock();

  36.     /* 更新當(dāng)前進(jìn)程運(yùn)行時(shí)間 */
  37.     update_curr(cfs_rq);

  38.     if (curr)
  39.         /* 將父進(jìn)程的虛擬運(yùn)行時(shí)間賦給了新進(jìn)程的虛擬運(yùn)行時(shí)間 */
  40.         se->vruntime = curr->vruntime;
  41.     /* 調(diào)整了se的虛擬運(yùn)行時(shí)間 */
  42.     place_entity(cfs_rq, se, 1);

  43.     if (sysctl_sched_child_runs_first && curr && entity_before(curr, se)) {
  44.         /*
  45.          * Upon rescheduling, sched_class::put_prev_task() will place
  46.          * 'current' within the tree based on its new key value.
  47.          */
  48.         swap(curr->vruntime, se->vruntime);
  49.         resched_curr(rq);
  50.     }

  51.     /* 保證了進(jìn)程p的vruntime是運(yùn)行隊(duì)列中最小的(這里占時(shí)不確定是不是這個(gè)用法,不過確實(shí)是最小的了) */
  52.     se->vruntime -= cfs_rq->min_vruntime;
  53.     
  54.     /* 解鎖,還原中斷記錄 */
  55.     raw_spin_unlock_irqrestore(&rq->lock, flags);
  56. }

  在task_fork_fair()函數(shù)中主要就是設(shè)置進(jìn)程p的虛擬運(yùn)行時(shí)間和所處的cfs隊(duì)列,值得我們注意的是 cfs_rq = task_cfs_rq(current); 這一行,在注釋中已經(jīng)表明task_cfs_rq(current)返回的是current的se.cfs_rq,注意se.cfs_rq保存的并不是根cfs隊(duì)列,而是所處的cfs_rq,也就是如果父進(jìn)程處于一個(gè)進(jìn)程組的cfs_rq中,新創(chuàng)建的進(jìn)程也會(huì)處于這個(gè)進(jìn)程組的cfs_rq中。

 

wake_up_new_task()

  到這里新進(jìn)程關(guān)于調(diào)度的初始化已經(jīng)完成,但是還沒有被調(diào)度器加入到隊(duì)列中,其是在do_fork()中的wake_up_new_task(p);中加入到隊(duì)列中的,我們具體看看wake_up_new_task()的實(shí)現(xiàn):


  1. void wake_up_new_task(struct task_struct *p)
  2. {
  3.     unsigned long flags;
  4.     struct rq *rq;

  5.     raw_spin_lock_irqsave(&p->pi_lock, flags);
  6. #ifdef CONFIG_SMP
  7.     /*
  8.      * Fork balancing, do it here and not earlier because:
  9.      * - cpus_allowed can change in the fork path
  10.      * - any previously selected cpu might disappear through hotplug
  11.      */
  12.      /* 為進(jìn)程選擇一個(gè)合適的CPU */
  13.     set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0));
  14. #endif

  15.     /* Initialize new task's runnable average */
  16.     /* 這里是跟多核負(fù)載均衡有關(guān) */
  17.     init_task_runnable_average(p);
  18.     /* 上鎖 */
  19.     rq = __task_rq_lock(p);
  20.     /* 將進(jìn)程加入到CPU的運(yùn)行隊(duì)列 */
  21.     activate_task(rq, p, 0);
  22.     /* 標(biāo)記進(jìn)程p處于隊(duì)列中 */
  23.     p->on_rq = TASK_ON_RQ_QUEUED;
  24.     /* 跟調(diào)試有關(guān) */
  25.     trace_sched_wakeup_new(p, true);
  26.     /* 檢查是否需要切換當(dāng)前進(jìn)程 */
  27.     check_preempt_curr(rq, p, WF_FORK);
  28. #ifdef CONFIG_SMP
  29.     if (p->sched_class->task_woken)
  30.         p->sched_class->task_woken(rq, p);
  31. #endif
  32.     task_rq_unlock(rq, p, &flags);
  33. }
  在wake_up_new_task()函數(shù)中,將進(jìn)程加入到運(yùn)行隊(duì)列的函數(shù)為activate_task(),而activate_task()函數(shù)最后會(huì)調(diào)用到新進(jìn)程調(diào)度類中的enqueue_task指針?biāo)负瘮?shù),這里我們具體看一下cfs調(diào)度類的enqueue_task指針?biāo)负瘮?shù)enqueue_task_fair():
  1. static void
  2. enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
  3. {
  4.     struct cfs_rq *cfs_rq;
  5.     struct sched_entity *se = &p->se;

  6.     /* 這里是一個(gè)迭代,我們知道,進(jìn)程有可能是處于一個(gè)進(jìn)程組中的,所以當(dāng)這個(gè)處于進(jìn)程組中的進(jìn)程加入到該進(jìn)程組的隊(duì)列中時(shí),要對(duì)此隊(duì)列向上迭代 */
  7.     for_each_sched_entity(se) {
  8.         if (se->on_rq)
  9.             break;
  10.         /* 如果不是CONFIG_FAIR_GROUP_SCHED,獲取其所在CPU的rq運(yùn)行隊(duì)列的cfs_rq運(yùn)行隊(duì)列
  11.          * 如果是CONFIG_FAIR_GROUP_SCHED,獲取其所在的cfs_rq運(yùn)行隊(duì)列
  12.          */
  13.         cfs_rq = cfs_rq_of(se);
  14.         /* 加入到隊(duì)列中 */
  15.         enqueue_entity(cfs_rq, se, flags);

  16.         /*
  17.          * end evaluation on encountering a throttled cfs_rq
  18.          *
  19.          * note: in the case of encountering a throttled cfs_rq we will
  20.          * post the final h_nr_running increment below.
  21.         */
  22.         if (cfs_rq_throttled(cfs_rq))
  23.             break;
  24.         cfs_rq->h_nr_running++;

  25.         flags = ENQUEUE_WAKEUP;
  26.     }

  27.     /* 只有se不處于隊(duì)列中或者cfs_rq_throttled(cfs_rq)返回真才會(huì)運(yùn)行這個(gè)循環(huán) */
  28.     for_each_sched_entity(se) {
  29.         cfs_rq = cfs_rq_of(se);
  30.         cfs_rq->h_nr_running++;

  31.         if (cfs_rq_throttled(cfs_rq))
  32.             break;

  33.         update_cfs_shares(cfs_rq);
  34.         update_entity_load_avg(se, 1);
  35.     }

  36.     if (!se) {
  37.         update_rq_runnable_avg(rq, rq->nr_running);
  38.         /* 當(dāng)前CPU運(yùn)行隊(duì)列活動(dòng)進(jìn)程數(shù) + 1 */
  39.         add_nr_running(rq, 1);
  40.     }
  41.     /* 設(shè)置下次調(diào)度中斷發(fā)生時(shí)間 */
  42.     hrtick_update(rq);
  43. }
  在enqueue_task_fair()函數(shù)中又使用了enqueue_entity()函數(shù)進(jìn)行操作,如下:
  1. static void
  2. enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
  3. {
  4.     /*
  5.      * Update the normalized vruntime before updating min_vruntime
  6.      * through calling update_curr().
  7.      */
  8.     if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
  9.         se->vruntime += cfs_rq->min_vruntime;

  10.     /*
  11.      * Update run-time statistics of the 'current'.
  12.      */
  13.     /* 更新當(dāng)前進(jìn)程運(yùn)行時(shí)間和虛擬運(yùn)行時(shí)間 */
  14.     update_curr(cfs_rq);
  15.     enqueue_entity_load_avg(cfs_rq, se, flags & ENQUEUE_WAKEUP);
  16.     /* 更新cfs_rq隊(duì)列總權(quán)重(就是在原有基礎(chǔ)上加上se的權(quán)重) */
  17.     account_entity_enqueue(cfs_rq, se);
  18.     update_cfs_shares(cfs_rq);

  19.     /* 新建的進(jìn)程flags為0,不會(huì)執(zhí)行這里 */
  20.     if (flags & ENQUEUE_WAKEUP) {
  21.         place_entity(cfs_rq, se, 0);
  22.         enqueue_sleeper(cfs_rq, se);
  23.     }

  24.     update_stats_enqueue(cfs_rq, se);
  25.     check_spread(cfs_rq, se);
  26.     
  27.     /* 將se插入到運(yùn)行隊(duì)列cfs_rq的紅黑樹中 */
  28.     if (se != cfs_rq->curr)
  29.         __enqueue_entity(cfs_rq, se);
  30.     /* 將se的on_rq標(biāo)記為1 */
  31.     se->on_rq = 1;

  32.     /* 如果cfs_rq的隊(duì)列中只有一個(gè)進(jìn)程,這里做處理 */
  33.     if (cfs_rq->nr_running == 1) {
  34.         list_add_leaf_cfs_rq(cfs_rq);
  35.         check_enqueue_throttle(cfs_rq);
  36.     }
  37. }

 

總結(jié)

  需要注意的幾點(diǎn):

  • 新創(chuàng)建的進(jìn)程先會(huì)進(jìn)行調(diào)度相關(guān)的結(jié)構(gòu)體和變量初始化,其中會(huì)根據(jù)不同的類型進(jìn)行不同的調(diào)度類操作,此時(shí)并沒有加入到隊(duì)列中。
  • 當(dāng)新進(jìn)程創(chuàng)建完畢后,它的父進(jìn)程會(huì)將其運(yùn)行狀態(tài)置為TASK_RUNNING,并加入到運(yùn)行隊(duì)列中。
  • 加入運(yùn)行隊(duì)列時(shí)系統(tǒng)會(huì)根據(jù)CPU的負(fù)載情況放入不同的CPU隊(duì)列中。






本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
網(wǎng)易博客歡迎您
理解進(jìn)程CFS組調(diào)度
【原創(chuàng)】(六)Linux進(jìn)程調(diào)度-實(shí)時(shí)調(diào)度器
linux2.6 CFS調(diào)度算法分析
Linux內(nèi)核之CFS調(diào)度和組調(diào)度
深入理解Linux內(nèi)核進(jìn)程的創(chuàng)建、調(diào)度和終止
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服