日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

在linux內(nèi)核中,調(diào)度器(scheduler)扮演著至關(guān)重要的角色,決定了哪個(gè)進(jìn)程將獲得CPU的執(zhí)行時(shí)間。本文將深入剖析內(nèi)核中調(diào)度器的代碼實(shí)現(xiàn),從入口函數(shù)開(kāi)始,一步步分析如何選擇下一個(gè)要執(zhí)行的進(jìn)程。讓我們一同揭開(kāi)這個(gè)內(nèi)核之謎。

Linux 內(nèi)核調(diào)度器源碼解析:從調(diào)度入口到挑選下一個(gè)進(jìn)程

調(diào)度器入口

Linux調(diào)度器入口函數(shù)定義在kernel/sched/core.c中:

asmlinkage __visible void __sched schedule(void)
{
    // 獲取當(dāng)前任務(wù)結(jié)構(gòu)體的指針
    struct task_struct *tsk = current;

    // 將任務(wù)提交到調(diào)度工作隊(duì)列中
    sched_submit_work(tsk);

    // 進(jìn)入調(diào)度循環(huán),直到?jīng)]有需要被調(diào)度的任務(wù)
    do {
        // 禁用搶占
        preempt_disable();
        // 調(diào)用實(shí)際的調(diào)度函數(shù) __schedule,并傳入調(diào)度策略參數(shù) SM_NONE
        __schedule(SM_NONE);
        // 啟用搶占,但不進(jìn)行重新調(diào)度
        sched_preempt_enable_no_resched();
    } while (need_resched()); // 循環(huán)直到?jīng)]有需要重新調(diào)度的任務(wù)

    // 更新工作隊(duì)列中的任務(wù)狀態(tài)
    sched_update_worker(tsk);
}
EXPORT_SYMBOL(schedule);

調(diào)度器的入口函數(shù)是schedule,首先獲取當(dāng)前任務(wù)結(jié)構(gòu)體的指針,然后將任務(wù)提交到調(diào)度工作隊(duì)列中,接著進(jìn)入一個(gè)循環(huán),該循環(huán)會(huì)禁用搶占,調(diào)用實(shí)際的調(diào)度函數(shù)__schedule,并在循環(huán)結(jié)束后啟用搶占。循環(huán)會(huì)一直執(zhí)行,直到?jīng)]有需要重新調(diào)度的任務(wù)為止。最后,函數(shù)會(huì)更新工作隊(duì)列中任務(wù)的狀態(tài)。函數(shù)最后export導(dǎo)出schedule函數(shù)以供其他部分使用。

static void __sched __schedule(bool preempt)
{
    struct task_struct *prev, *next;
    unsigned long *switch_count;
    struct rq *rq;

    prev = current;
    rq = this_rq();
    switch_count = &prev->nivcsw;

    // 獲取下一個(gè)要運(yùn)行的進(jìn)程
    next = pick_next_task(rq);

    // 切換到下一個(gè)進(jìn)程
    context_switch(rq, prev, next, switch_count);

    // 如果需要搶占,啟用搶占
    if (preempt)
        need_resched();
}

} 這里,__schedule函數(shù)負(fù)責(zé)實(shí)際的調(diào)度操作。首先,它獲取了當(dāng)前任務(wù)結(jié)構(gòu)體的指針(prev)、運(yùn)行隊(duì)列(rq)以及切換計(jì)數(shù)器(switch_count)。然后,通過(guò)調(diào)用pick_next_task函數(shù),它選擇下一個(gè)要運(yùn)行的進(jìn)程(next)。最后,通過(guò)context_switch函數(shù),它進(jìn)行進(jìn)程切換,將CPU控制權(quán)移交給下一個(gè)進(jìn)程。

具體如何挑選下一個(gè)需要運(yùn)行的進(jìn)程,就要扒開(kāi)pick_next_task函數(shù)。

pick_next_task

/*
 * 選擇下一個(gè)要運(yùn)行的任務(wù)。
 */
static inline struct task_struct *
__pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
    const struct sched_class *class; // 定義調(diào)度類(lèi)指針
    struct task_struct *p; // 定義任務(wù)結(jié)構(gòu)體指針

    // 優(yōu)化:如果前一個(gè)任務(wù)是公平調(diào)度類(lèi)中的任務(wù),且運(yùn)行隊(duì)列中的任務(wù)數(shù)與CFS隊(duì)列中的任務(wù)數(shù)相等,
    // 則可以直接選擇下一個(gè)公平類(lèi)任務(wù),因?yàn)槠渌{(diào)度類(lèi)的任務(wù)無(wú)法搶占CPU。
    if (likely(!sched_class_above(prev->sched_class, &fAIr_sched_class) &&
               rq->nr_running == rq->cfs.h_nr_running)) {

        p = pick_next_task_fair(rq, prev, rf); // 選擇下一個(gè)公平調(diào)度類(lèi)任務(wù)
        if (unlikely(p == RETRY_TASK)) // 如果選擇任務(wù)失敗,需要重新嘗試
            goto restart;

        if (!p) {
            put_prev_task(rq, prev);
            p = pick_next_task_idle(rq); // 如果沒(méi)有可運(yùn)行任務(wù),則選擇下一個(gè)空轉(zhuǎn)調(diào)度類(lèi)任務(wù)
        }

        return p;
    }

restart:
    put_prev_task_balance(rq, prev, rf); // 將前一個(gè)任務(wù)放回隊(duì)列,進(jìn)行重新平衡

    // 遍歷所有調(diào)度類(lèi)
    for_each_class(class) {
        p = class->pick_next_task(rq); // 選擇下一個(gè)任務(wù)
        if (p)
            return p;
    }

    BUG(); // 如果沒(méi)有可運(yùn)行任務(wù),引發(fā)BUG??辙D(zhuǎn)類(lèi)應(yīng)該始終有可運(yùn)行的任務(wù)。
}

這段代碼是用于選擇下一個(gè)要運(yùn)行的任務(wù)的函數(shù)。首先,它檢查是否可以?xún)?yōu)化選擇下一個(gè)任務(wù),如果前一個(gè)任務(wù)是公平調(diào)度類(lèi)中的任務(wù),并且運(yùn)行隊(duì)列中的任務(wù)數(shù)與CFS隊(duì)列中的任務(wù)數(shù)相等,就可以直接選擇下一個(gè)公平調(diào)度類(lèi)任務(wù)。如果選擇任務(wù)失敗,會(huì)重新嘗試,然后如果沒(méi)有可運(yùn)行任務(wù),將選擇下一個(gè)空轉(zhuǎn)調(diào)度類(lèi)任務(wù)。如果不滿(mǎn)足優(yōu)化條件,將會(huì)重新平衡隊(duì)列,然后遍歷所有的調(diào)度類(lèi),選擇下一個(gè)任務(wù)。如果沒(méi)有可運(yùn)行任務(wù),將引發(fā)BUG,因?yàn)榭辙D(zhuǎn)類(lèi)應(yīng)該始終有可運(yùn)行的任務(wù)。


struct task_struct *
pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
  struct cfs_rq *cfs_rq = &rq->cfs; // 獲取CFS隊(duì)列
  struct sched_entity *se; // 定義調(diào)度實(shí)體指針
  struct task_struct *p; // 定義任務(wù)結(jié)構(gòu)體指針
  int new_tasks;

again:
  // 如果沒(méi)有可運(yùn)行的公平調(diào)度任務(wù),跳轉(zhuǎn)到idle標(biāo)簽
  if (!sched_fair_runnable(rq))
    goto idle;

#ifdef CONFIG_FAIR_GROUP_SCHED
  // 如果沒(méi)有前一個(gè)任務(wù),或者前一個(gè)任務(wù)不屬于公平調(diào)度類(lèi),跳轉(zhuǎn)到simple標(biāo)簽
  if (!prev || prev->sched_class != &fair_sched_class)
    goto simple;

  do {
    struct sched_entity *curr = cfs_rq->curr;

    // 如果當(dāng)前任務(wù)存在
    if (curr) {
      // 如果當(dāng)前任務(wù)在隊(duì)列上,則更新其運(yùn)行時(shí)間
      if (curr->on_rq)
        update_curr(cfs_rq);
      else
        curr = NULL;

      // 如果CFS隊(duì)列的運(yùn)行時(shí)間不正常,跳轉(zhuǎn)到idle標(biāo)簽
      if (unlikely(check_cfs_rq_runtime(cfs_rq))) {
        cfs_rq = &rq->cfs;

        // 如果沒(méi)有可運(yùn)行任務(wù),跳轉(zhuǎn)到idle標(biāo)簽
        if (!cfs_rq->nr_running)
          goto idle;

        goto simple;
      }
    }

    // 選擇下一個(gè)調(diào)度實(shí)體,并切換到相應(yīng)的CFS隊(duì)列
    se = pick_next_entity(cfs_rq, curr);
    cfs_rq = group_cfs_rq(se);
  } while (cfs_rq);

  // 獲取與選定實(shí)體關(guān)聯(lián)的任務(wù)結(jié)構(gòu)體
  p = task_of(se);

  // 如果前一個(gè)任務(wù)不等于選定任務(wù),進(jìn)行任務(wù)切換
  if (prev != p) {
    struct sched_entity *pse = &prev->se;

    while (!(cfs_rq = is_same_group(se, pse))) {
      int se_depth = se->depth;
      int pse_depth = pse->depth;

      if (se_depth <= pse_depth) {
        put_prev_entity(cfs_rq_of(pse), pse);
        pse = parent_entity(pse);
      }
      if (se_depth >= pse_depth) {
        set_next_entity(cfs_rq_of(se), se);
        se = parent_entity(se);
      }
    }

    put_prev_entity(cfs_rq, pse);
    set_next_entity(cfs_rq, se);
  }

  goto done;
simple:
#endif
  // 如果有前一個(gè)任務(wù),將其放回隊(duì)列
  if (prev)
    put_prev_task(rq, prev);

  do {
    // 選擇下一個(gè)調(diào)度實(shí)體,并切換到相應(yīng)的CFS隊(duì)列
    se = pick_next_entity(cfs_rq, NULL);
    set_next_entity(cfs_rq, se);
    cfs_rq = group_cfs_rq(se);
  } while (cfs_rq);

  // 獲取與選定實(shí)體關(guān)聯(lián)的任務(wù)結(jié)構(gòu)體
  p = task_of(se);

done: __maybe_unused;

#ifdef CONFIG_SMP
  // 將下一個(gè)正在運(yùn)行的任務(wù)移動(dòng)到隊(duì)列的前面
  list_move(&p->se.group_node, &rq->cfs_tasks);
#endif

  // 如果啟用高精度定時(shí)器,開(kāi)始高精度定時(shí)
  if (hrtick_enabled_fair(rq))
    hrtick_start_fair(rq, p);

  // 更新不適合運(yùn)行的任務(wù)狀態(tài)
  update_misfit_status(p, rq);

  return p;

idle:
  // 如果沒(méi)有rf標(biāo)志,返回NULL
  if (!rf)
    return NULL;

  // 嘗試進(jìn)行新的空閑平衡操作
  new_tasks = newidle_balance(rq, rf);

  // 如果新的平衡操作失敗,返回RETRY_TASK標(biāo)志
  if (new_tasks < 0)
    return RETRY_TASK;

  // 如果有新的可運(yùn)行任務(wù),回到again標(biāo)簽重新選擇
  if (new_tasks > 0)
    goto again;

  // 如果隊(duì)列即將變?yōu)榭臻e狀態(tài),檢查是否需要更新時(shí)鐘pelt的lost_idle_time
  update_idle_rq_clock_pelt(rq);

  return NULL;
}

這個(gè)函數(shù)用于選擇下一個(gè)要在公平調(diào)度類(lèi)中運(yùn)行的任務(wù)。函數(shù)中包含了條件判斷和循環(huán),以確保選擇最適合的任務(wù)。


/*
 * 選擇下一個(gè)調(diào)度實(shí)體,考慮以下因素,按照順序:
 * 1) 在進(jìn)程/任務(wù)組之間保持公平性
 * 2) 選擇“下一個(gè)”進(jìn)程,因?yàn)槟硞€(gè)進(jìn)程確實(shí)希望運(yùn)行
 * 3) 選擇“上一個(gè)”進(jìn)程,以提高緩存局部性
 * 4) 如果其他任務(wù)可用,則不運(yùn)行“跳過(guò)”的進(jìn)程
 */
static struct sched_entity *
pick_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
  struct sched_entity *left = __pick_first_entity(cfs_rq);  // 獲取最左邊的實(shí)體
  struct sched_entity *se;

  /*
   * 如果 curr 被設(shè)置,我們必須查看它是否位于樹(shù)中最左邊的實(shí)體的左側(cè),
   * 前提是樹(shù)中確實(shí)有實(shí)體存在。
   */
  if (!left || (curr && entity_before(curr, left)))
    left = curr;

  se = left; /* 理想情況下,我們運(yùn)行最左邊的實(shí)體 */

  /*
   * 避免運(yùn)行跳過(guò)的實(shí)體,如果可以不運(yùn)行其他實(shí)體而不會(huì)太不公平。
   */
  if (cfs_rq->skip && cfs_rq->skip == se) {
    struct sched_entity *second;

    if (se == curr) {
      second = __pick_first_entity(cfs_rq);  // 獲取最左邊的實(shí)體
    } else {
      second = __pick_next_entity(se);  // 獲取下一個(gè)實(shí)體
      if (!second || (curr && entity_before(curr, second)))
        second = curr;
    }

    if (second && wakeup_preempt_entity(second, left) < 1)
      se = second;
  }

  if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1) {
    /*
     * 有人確實(shí)希望運(yùn)行這個(gè)實(shí)體。如果不不公平,就運(yùn)行它。
     */
    se = cfs_rq->next;
  } else if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1) {
    /*
     * 更傾向于運(yùn)行最后一個(gè)實(shí)體,嘗試將 CPU 返回到一個(gè)被搶占的任務(wù)。
     */
    se = cfs_rq->last;
  }

  return se;
}
 
if (se == curr) {
  second = __pick_first_entity(cfs_rq);  // 獲取最左邊的實(shí)體
} else {
  second = __pick_next_entity(se);  // 獲取下一個(gè)實(shí)體
  if (!second || (curr && entity_before(curr, second)))
    second = curr;
}

if (second && wakeup_preempt_entity(second, left) < 1)
  se = second;

return se; } 函數(shù)pick_next_entity的作用是選擇下一個(gè)要運(yùn)行的調(diào)度實(shí)體,它根據(jù)一系列因素來(lái)決定選擇哪個(gè)實(shí)體,以確保公平性、滿(mǎn)足任務(wù)需求,并盡量提高緩存局部性。

總結(jié)

通過(guò)深入分析Linux內(nèi)核調(diào)度器的代碼實(shí)現(xiàn),我們了解了調(diào)度器的入口函數(shù)和選擇下一個(gè)執(zhí)行進(jìn)程的過(guò)程。這個(gè)過(guò)程是內(nèi)核多任務(wù)處理的核心,確保了系統(tǒng)資源的合理分配。深入理解調(diào)度器的工作原理將有助于我們更好地優(yōu)化系統(tǒng)性能,提高響應(yīng)速度。

分享到:
標(biāo)簽:Linux
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定