<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

帶大家看看Linux內核如何調度進程的

B4Pb_gh_6fde77c ? 來源:Linux內核遠航者 ? 作者:Linux內核遠航者 ? 2021-07-26 15:14 ? 次閱讀

1.開場白

環境:

處理器架構:arm64

內核源碼:linux-5.11

ubuntu版本:20.04.1

代碼閱讀工具:vim+ctags+cscope

本文步進到Linux內核進程管理的核心部分,打開調度器的黑匣子,來看看Linux內核如何調度進程的。實際上,進程調度器主要做兩件事:選擇下一個進程,然后進行上下文切換。

而何時調用主調度器調度進程那是調度時機所關注的問題,而調度時機在之前的內核搶占文章已經做了詳細講解,在此不在贅述,而本文關注的調度時機是真正調用主調度器的時機。

本文分析的內核源代碼主要集中在:

kernel/sched/core.c

kernel/sched/fair.c

2.調用時機

關于調度時機,網上的文章也五花八門,之前在內核搶占文章已經做了詳細講解,而在本文我們從源碼注釋中給出依據(再次強調一下:本文的調度時機關注的是何時調用主調度器,不是設置重新調度標志的時機,之前講解中我們知道他們都可以稱為調度時機)。

先來說一下什么是主調度器,其實和主調度器并列的還有一個叫做周期性調度器的東西(后面有機會會講解,主要用于時鐘中斷tick調來使奪取處理器的控制權),他們都是內核中的一個函數,在合適的時機被調用。

主調度器函數如下:

kernel/sched/core.c __schedule()

內核的很多路徑會包裝這個函數,主要分為主動調度和搶占式調度場景。

內核源碼中主調度器函數也給出了調度時機的注釋,下面我們就以此為依據來看下:

kernel/sched/core.c /* *__schedule()isthemainschedulerfunction. * *Themainmeansofdrivingtheschedulerandthusenteringthisfunctionare: * *1.Explicitblocking:mutex,semaphore,waitqueue,etc. * *2.TIF_NEED_RESCHEDflagischeckedoninterruptanduserspacereturn *paths.Forexample,seearch/x86/entry_64.S. * *Todrivepreemptionbetweentasks,theschedulersetstheflagintimer *interrupthandlerscheduler_tick(). * *3.Wakeupsdon'treallycauseentryintoschedule().Theyadda *tasktotherun-queueandthat'sit. * *Now,ifthenewtaskaddedtotherun-queuepreemptsthecurrent *task,thenthewakeupsetsTIF_NEED_RESCHEDandschedule()gets *calledonthenearestpossibleoccasion: * *-Ifthekernelispreemptible(CONFIG_PREEMPTION=y): * *-insyscallorexceptioncontext,atthenextoutmost *preempt_enable().(thismightbeassoonasthewake_up()'s *spin_unlock()!) * *-inIRQcontext,returnfrominterrupt-handlerto *preemptiblecontext * *-Ifthekernelisnotpreemptible(CONFIG_PREEMPTIONisnotset) *thenatthenext: *-cond_resched()call *-explicitschedule()call *-returnfromsyscallorexceptiontouser-space *-returnfrominterrupt-handlertouser-space * *WARNING:mustbecalledwithpreemptiondisabled! */ staticvoid__schednotrace__schedule(boolpreempt)

我們對注釋做出解釋,讓大家深刻理解調度時機(基本上是原樣翻譯,用顏色標注)。

1.顯式阻塞場景:包括互斥體、信號量、等待隊列等。

這個場景主要是為了等待某些資源而主動放棄處理器,來調用主調度器,如發現互斥體被其他內核路徑所持有,則睡眠等待互斥體被釋放的時候來喚醒我。

2.在中斷和用戶空間返回路徑上檢查TIF_NEED_RESCHED標志。例如,arch/x86/entry_64.S。為了在任務之間驅動搶占,調度程序在計時器中斷處理程序scheduler_tick()中設置標志。

解釋如下:這實際上是說重新調度標志(TIF_NEED_RESCHED)的設置和檢查的情形。

1)重新調度標志設置情形:如scheduler_tick周期性調度器按照特定條件設置、喚醒的路徑上按照特定條件設置等。當前這樣的場景并不會直接調用主調度器,而會在最近的調度點到來時調用主調度器。

2)重新調度標志檢查情形:是真正的調用主調度器,下面的場景都會涉及到,在此不在贅述。

3.喚醒并不會真正導致schedule()的進入。他們添加一個任務到運行隊列,僅此而已。

現在,如果添加到運行隊列中的新任務搶占了當前任務,那么喚醒設置TIF_NEED_RESCHED, schedule()在最近的可能情況下被調用:

1)如果內核是可搶占的(CONFIG_PREEMPTION=y)

-在系統調用或異常上下文中,最外層的preempt_enable()。(這可能和wake_up()的spin_unlock()一樣快!)

-在IRQ上下文中,從中斷處理程序返回到搶占上下文

注釋中很簡潔的幾句話,但其中的含義需要深刻去體會。

首先需要知道一點是:內核搶占說的是處于內核態的任務被其他任務所搶占的情況(無論是不是可搶占式內核,處于用戶態的任務都可以被搶占,處于內核態的任務是否能被搶占由是否開啟內核搶占來決定),當然內核態的任務可以是內核線程也可以是通過系統調用請求內核服務的用戶任務。

情況1:這是重新開啟內核搶占的情況,即是搶占計數器為0時,檢查重新調度標志(TIF_NEED_RESCHED),如果設置則調用主調度器,放棄處理器(這是搶占式調度)。

情況2:中斷返回內核態的時候,檢查重新調度標志(TIF_NEED_RESCHED),如果設置且搶占計數器為0時則調用主調度器,放棄處理器(這是搶占式調度)。

注:關于內核搶占可以參考之前發布的文章。

2)如果內核是不可搶占的(CONFIG_PREEMPTION=y)

cond_resched()調用

顯式的schedule()調用

從系統調用或異常返回到用戶空間

從中斷處理器返回到用戶空間

解釋如下:

cond_resched()是為了在不可搶占內核的一些耗時的內核處理路徑中增加主動搶占點(搶占計數器是否為0且當前任務被設置了重新調度標志),則調用主調度器進行搶占式調度,所進行低延時處理。

顯式的schedule()調用,這是主動放棄處理器的場景,如一些睡眠場景,像用戶任務調用sleep。

系統調用或異常返回到用戶空間使會判斷當前進程是否設置重新調度標志(TIF_NEED_RESCHED),如果設置則調用主調度器,放棄處理器。

中斷處理器返回到用戶空間會判斷當前進程是否設置重新調度標志(TIF_NEED_RESCHED),如果設置則調用主調度器,放棄處理器。

其實還有一種場景也會調用到主調度器讓出處理器,那就是進程退出時,這里不在贅述。

下面給出總結:

1.主動調度:

睡眠場景,如sleep。

顯式阻塞場景,如互斥體,信號量,等待隊列,完成量等。

任務退出時,調用do_exit去釋放進程資源,最后會調用一次主調度器

2.搶占調度:

不可搶占式內核

cond_resched()調用

顯式的schedule()調用

從系統調用或異常返回到用戶空間

從中斷處理器返回到用戶空間

可搶占式內核(增加一些搶占點)

重新開啟內核搶占

中斷返回內核態的時候

3.主調度器調用時機源碼窺探

下面給出主要的一些主調度器調用時機源碼分析,作為學習參考。

3.1 常規場景

中斷返回用戶態場景:

arch/arm64/kernel/entry.S el0_irq ->ret_to_user ->work_pending ->do_notify_resume ->if(thread_flags&_TIF_NEED_RESCHED){//arch/arm64/kernel/signal.c schedule(); ->__schedule(false);//kernel/sched/core.cfalse表示主動調度

異常返回用戶態場景:

arch/arm64/kernel/entry.S el0_sync ->ret_to_user ...

任務退出場景:

kernel/exit.c do_exit ->do_task_dead ->__schedule(false);//kernel/sched/core.cfalse表示主動調度

顯式阻塞場景(舉例互斥體):

kernel/locking/mutex.c mutex_lock ->__mutex_lock_slowpath ->__mutex_lock ->__mutex_lock_common ->schedule_preempt_disabled ->schedule(); ->__schedule(false);//kernel/sched/core.cfalse表示主動調度

3.2 支持內核搶占場景

中斷返回內核態場景

arch/arm64/kernel/entry.S el1_irq #ifdefCONFIG_PREEMPTION ->arm64_preempt_schedule_irq ->preempt_schedule_irq(); ->__schedule(true);//kernel/sched/core.ctrue表示搶占式調度 #endif

內核搶占開啟場景

preempt_enable ->if(unlikely(preempt_count_dec_and_test()))//搶占計數器減一為0 __preempt_schedule(); ->preempt_schedule//kernel/sched/core.c ->__schedule(true)//調用主調度器進行搶占式調度

注:一般說異常/中斷返回,返回是處理器異常狀態,可能是用戶態也可能是內核態,但是會看到很多資料寫的都是用戶空間/內核空間并不準確,但是我們認為表達一個意思,做的心中有數即可。

3.選擇下一個進程

本節主要講解主調度器是如何選擇下一個進程的,這和調度策略強相關。

下面我們來看具體實現:

kernel/sched/core.c __schedule ->next=pick_next_task(rq,prev,&rf); ->if(likely(prev->sched_class<=?&fair_sched_class?&&?????????????? ????????|??rq->nr_running==rq->cfs.h_nr_running)){ p=pick_next_task_fair(rq,prev,rf); if(unlikely(p==RETRY_TASK)) gotorestart; /*Assumesfair_sched_class->next==idle_sched_class*/ if(!p){ put_prev_task(rq,prev); p=pick_next_task_idle(rq); } returnp; } for_each_class(class){ p=class->pick_next_task(rq); if(p) returnp; }

這里做了優化,當當前進程的調度類為公平調度類或者空閑調度類時,且cpu運行隊列的進程個數等于cfs運行隊列進程個數,說明運行隊列進程都是普通進程,則直接調用公平調度類的pick_next_task_fair選擇下一個進程(選擇紅黑樹最左邊的那個進程),如果沒有找到說明當前進程調度類為空閑調度類,直接調用pick_next_task_idle選擇idle進程。

否則,遍歷調度類,從高優先級調度類開始調用其pick_next_task方法選擇下一個進程。

下面以公平調度類為例來看如何選擇下一個進程的:調用過程如下(這里暫不考慮組調度情況):

pick_next_task ->pick_next_task_fair//kernel/sched/fair.c ->if(prev) put_prev_task(rq,prev); se=pick_next_entity(cfs_rq,NULL); set_next_entity(cfs_rq,se);

先看put_prev_task:

put_prev_task ->prev->sched_class->put_prev_task(rq,prev); ->put_prev_task_fair ->put_prev_entity(cfs_rq,se); ->/*Put'current'backintothetree.*/ __enqueue_entity(cfs_rq,prev); cfs_rq->curr=NULL;

這里會調用__enqueue_entity將前一個進程重新加入到cfs隊列的紅黑樹。然后將cfs_rq->curr 設置為空。

再看pick_next_entity:

pick_next_entity ->left=__pick_first_entity(cfs_rq); ->left=rb_first_cached(&cfs_rq->tasks_timeline);

將選擇cfs隊列紅黑樹最左邊進程。

最后看set_next_entity:

set_next_entity ->__dequeue_entity(cfs_rq,se); ->cfs_rq->curr=se;

這里調用__dequeue_entity將下一個選擇的進程從cfs隊列的紅黑樹中刪除,然后將cfs隊列的curr指向進程的調度實體。

選擇下一個進程總結如下:

運行隊列中只有公平進程則選擇公平調度類的pick_next_task_fair選擇進程。

當前進程為idle進程,且沒有公平進程存在情況下,調用pick_next_task_idle選擇idle進程。

運行隊列存在除了公平進程的其他進程,則從高優先級到低優先級調用具體調度類的pick_next_task選擇進程。

對于公平調度類,選擇下一個進程主要過程如下:1)調用put_prev_task方法將前一個進程重新加入cfs隊列的紅黑樹。2)調用pick_next_entity 選擇紅黑樹最左邊的進程作為下一個進程。3)將下一個進程從紅黑樹中刪除,cfs隊列的curr指向進程的調度實體。

通用的調度類選擇順序為:

stop_sched_class ->dl_sched_class->rt_sched_class->fair_sched_class ->idle_sched_class

比如:當前運行隊列都是cfs的普通進程,某一時刻發生中斷喚醒了一個rt進程,那么在最近的調度點到來時就會調用主調度器選擇rt進程作為next進程。

做了以上的工作之后,紅黑樹中選擇下一個進程的時候就不會再選擇到當前cpu上運行的進程了,而當前進程調度實體又被cfs隊列的curr來記錄著(運行隊列的curr也會記錄當前進程)。

下面給出公平調度類選擇下一個進程圖解(其中A為前一個進程,即是當前進程,即為前一個進程,B為下一個進程)

編輯:jq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11025

    瀏覽量

    207145

原文標題:深入理解Linux內核之主調度器(上)

文章出處:【微信號:gh_6fde77c41971,微信公眾號:FPGA干貨】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    linux內核主要由哪幾個部分組成,作用是什么

    Linux內核主要由以下幾個部分組成: 進程管理:Linux內核負責管理和調度系統中的
    的頭像 發表于 01-22 14:34 ?1365次閱讀

    Linux內核中信號的傳遞過程

    前面我們已經介紹了內核注意到信號的到來,調用相關函數更新進程描述符以便進程接收處理信號。但是,如果目標進程此時沒有運行,內核則推遲傳遞信號。
    的頭像 發表于 01-17 09:51 ?596次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>中信號的傳遞過程

    兆芯正引入Linux首選內核調度技術,優化性能

    近期,兆芯工程團隊亦在致力于將首選內核調度技術引進Linux系統中。他們試圖通過提議的Linux內核補丁,利用已有的ACPI功能來辨別每個核
    的頭像 發表于 12-29 14:30 ?281次閱讀
    兆芯正引入<b class='flag-5'>Linux</b>首選<b class='flag-5'>內核</b><b class='flag-5'>調度</b>技術,優化性能

    linux查看weblogic進程

    Linux操作系統中,WebLogic是一種常用的Java應用服務器,用于部署和管理企業級Java應用程序。為了確保WebLogic服務器正常運行,有時我們需要查看WebLogic進程以了解其狀態
    的頭像 發表于 12-05 16:07 ?962次閱讀

    Linux內核UDP收包為什么效率低

    現在很多人都在詬病Linux內核協議棧收包效率低,不管他們是真的懂還是一點都不懂只是聽別人說的,反正就是在一味地懟Linux內核協議棧,他們的武器貌似只有DPDK。 但是,即便
    的頭像 發表于 11-13 10:38 ?279次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>UDP收包為什么效率低

    如何優化Linux內核UDP收包效率低

    很多人都在詬病Linux內核協議棧收包效率低,不管他們是真的懂還是一點都不懂只是聽別人說的,反正就是在一味地懟Linux內核協議棧,他們的武器貌似只有DPDK。 但是,
    的頭像 發表于 11-10 10:51 ?323次閱讀
    如何優化<b class='flag-5'>Linux</b><b class='flag-5'>內核</b>UDP收包效率低

    什么是Linux進程調度

    片)。調度器使得我們同時執行多個程序成為可能,因此可以與具有各種需求的用戶共享CPU。 內核必須提供一種方法, 在各個進程之間盡可能公平地共享CPU時間, 而同時又要考慮不同的任務優先級. 調
    的頭像 發表于 11-09 09:05 ?323次閱讀
    什么是<b class='flag-5'>Linux</b><b class='flag-5'>進程</b><b class='flag-5'>調度</b>器

    Linux內核死鎖lockdep功能

    死鎖是指兩個或多個進程因爭奪資源而造成的互相等待的現象,如進程A需要資源X,進程B需要資源Y,而雙方都掌握對方所需要的資源,且都不釋放,這會導致死鎖。 在內核開發中,時常要考慮并發設計
    的頭像 發表于 09-27 15:13 ?435次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>死鎖lockdep功能

    Linux內核如何使用結構體和函數指針?

    我將結合具體的Linux內核驅動框架代碼來展示Linux內核如何使用結構體和函數指針。
    的頭像 發表于 09-06 14:17 ?628次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>如何使用結構體和函數指針?

    Linux進程相關知識

    進程是在你的系統上運行的程序。它們由內核管理,每個進程都有一個與之關聯的ID,稱為進程ID(PID)。這個PID是按照進程創建的順序分配的。
    發表于 08-09 10:02 ?221次閱讀
    <b class='flag-5'>Linux</b>下<b class='flag-5'>進程</b>相關知識

    Linux下查詢進程占用的內存方法總結

    今天浩道跟大家一篇關于運維牛人如何在Linux下挖出吃內存的進程,可以說是相當干的一個運維技能了,一起看看吧!
    發表于 07-27 10:51 ?1.2w次閱讀
    <b class='flag-5'>Linux</b>下查詢<b class='flag-5'>進程</b>占用的內存方法總結

    Linux內核的作用

    Linux操作系統是當今世界上最為廣泛使用的開源操作系統之一,內核則是一個操作系統的核心和靈魂所在。對于一名Linux驅動開發者來說,了解Linux
    發表于 07-06 11:46 ?1398次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的作用

    Linux內核內存泄漏怎么辦

    Linux內核開發中,Kmemleak是一種用于檢測內核中內存泄漏的工具。
    發表于 07-04 11:04 ?635次閱讀

    linux內核源碼編譯

    ,也不能對Minix開發者所作的設計進行修改,基于此linus開始了開發自己的操作系統,并于1991年年底在internet上發布了早期版本,由此Linux內核誕生。Linux內核的主
    發表于 06-27 15:37 ?628次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內核</b>源碼編譯

    linux操作系統中的進程創建和銷毀函數解析

    第一次遇見創建進程是在Linux啟動流程中,reset_init函數調用kernel_thread函數創建了2個內核進程:kernel_init和kthreadd。
    發表于 06-26 09:12 ?449次閱讀
    <b class='flag-5'>linux</b>操作系統中的<b class='flag-5'>進程</b>創建和銷毀函數解析
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>