天天看點

十天學Linux核心之第六天---排程和核心同步

  心情大好,昨晚我們實驗室老大和我們聊了好久,作為已經在實驗室待了快兩年的大三工科男來說,老師讓我們不要成為那種技術狗,代碼工,說多了都是淚啊,,不過我們的激情依舊不變,老師幫我們組好了隊伍,着手參加明年的全國大賽,說起來我們學校曆史上也就又一次拿國一的,去了一次人民大會堂領獎,可以說老大是對我們寄予厚望,以後我會專攻儀器儀表類的題目,激情不滅,夢想不息,不過最近一段時間還是會繼續更新Linux核心,總之,繼續加油~

  Linux2.6版本中的核心引入了一個全新的排程程式,稱為O(1)排程程式,程序在被初始化并放到運作隊列後,在某個時刻應該獲得對CPU的通路,它負責把CPU的控制權傳遞到不同程序的兩個函數schedule()和schedule_tick()中。下圖是随着時間推移,CPU是如何在不同程序之間傳遞的,至于細節這裡不多闡釋,大家看待就可以了解的啦~

十天學Linux核心之第六天---排程和核心同步

  下面開始介紹一下上下文切換,在作業系統中,CPU切換到另一個程序需要儲存目前程序的狀态并恢複另一個程序的狀态:目前運作任務轉為就緒(或者挂起、删除)狀态,另一個被標明的就緒任務成為目前任務。上下文切換包括儲存目前任務的運作環境,恢複将要運作任務的運作環境。

 如何獲得上下文切換的次數?

  vmstat直接運作即可,在最後幾列,有CPU的context switch次數。 這個是系統層面的,加入想看特定程序的情況,可以使用pidstat。

​執行pidstat,将輸出系統啟動後所有活動程序的cpu統計資訊:  

上下文切換的性能消耗在哪裡呢?

    ​    ​context switch過高,會導緻CPU像個搬運工,頻繁在寄存器和運作隊列直接奔波  ,更多的時間花在了線程切換,而不是真正工作的線程上。直接的消耗包括CPU寄存器需要儲存和加載,系統排程器的代碼需要執行。間接消耗在于多核cache之間的共享資料。    ​

引起上下文切換的原因有哪些?

對于搶占式作業系統而言, 大體有幾種:

目前任務的時間片用完之後,系統CPU正常排程下一個任務;

目前任務碰到IO阻塞,排程線程将挂起此任務,繼續下一個任務;

多個任務搶占鎖資源,目前任務沒有搶到,被排程器挂起,繼續下一個任務;

使用者代碼挂起目前任務,讓出CPU時間;

硬體中斷;    ​   ​

​如何測試上下文切換的時間消耗?

  這裡我再網上查找到了一個程式,代碼不長,切換一個   差不多就是  20個微秒吧,這個程式望大神指教

  總而言之,我們可以認為,這最多隻能是依賴于底層作業系統的近似計算。 一個近似的解法是記錄一個程序結束時的時間戳,另一個程序開始的時間戳及排除等待時間。如果所有程序總共用時為T,那麼總的上下文切換時間為: T – (所有程序的等待時間和執行時間)

  接下來來述說搶占,搶占是一個程序到另一個程序的切換,那麼Linux是如何決定在何時進行切換的呢?下面我們一次來介紹這三種搶占方式。

顯式核心搶占:

  最容易的就是這個搶占啦,它發生在核心代碼調用schedule(可以直接調用或者阻塞調用)時候的核心空間中。當這種方式搶占時,例如在wait_queue等待隊列中裝置驅動程式在等候時,控制權被簡單地傳遞到排程程式,進而新的程序被選中執行。

隐式使用者搶占:

  當核心處理完核心空間的程序并準備把控制權傳遞到使用者空間的程序時,它首先檢視應該把控制權傳遞到哪一個使用者空間的程序上,這個程序也行不是傳遞其控制權到核心的那個使用者空間程序。系統中中的每一個程序有一個“必須重新排程”,在程序應該被重新排程的任何時候設定它。代碼可以在include/linux/sched.h中檢視~

隐式核心搶占:

  隐式核心搶占有兩種可能性:核心代碼出自使搶占禁止的代碼塊,或者處理正在從中斷傳回到核心代碼時,如果控制權正在從一個中斷傳回到核心空間,該中斷調用schedule(),一個新的 程序以剛才描述的同一種方式被選中,如果核心代碼出自禁止搶占的代碼塊,激活搶占 的操作可能引起目前程序被搶占(代碼在include/linux/preempt.h中檢視到):

  preempt_enable()調用preempt_enable_no_resched(),它把與目前程序相關的preempt_count減1,然後調用preempt_check_resched():

  自旋鎖被初始化後,可以通過調用spin_lock()或者spin_lock_irqsave()來擷取,如果你使用spin_lock()那麼程序可能在上鎖的代碼中被中斷,為了在代碼的臨界區執行後釋放,必須調用spin_unlock()或者spin_unlock_irqrestroe(),spin_unlock_irqrestroe()把中斷寄存器的狀态恢複成調用spin_lock_irq()時寄存器所處的狀态。

  自旋鎖的缺點是它們頻繁地循環直到等待鎖的釋放,那麼對于等待時間長的代碼區,最好是使用Linux kernel的另一個上鎖工具:信号量。它的主要優勢之一是:持有信号量的程序可以安全的阻塞,它們在SMP和中斷中是保險的(代碼在include/asm-i386/semaphore.h,include/asm-ppc/semaphore.h中可以檢視):

  l兩種體系結構的實作都提供了指向wait_queue的一個指針和一個計數,有了信号量我們能夠讓多于一個的程序同時進去代碼的臨界區,如果計數初始化為1,則表示隻有一個程序能夠進去代碼的臨界區,信号量用sema_init()來初始化,分别調用down()和up()來上鎖和解鎖,down()和up()函數的使用以及一些情況就不多說了看信号量的調用問題,很容易了解的。

  小結

  好吧我真的自己不懂的越來越多了,覺得一天天的接受不了這麼多,核心實在是塊難啃的石頭,今天這個可是查了好多一整天才寫出來的,肯定有很多問題以及沒有提到的地方,各路大神路過時候多指點多批評,,對一個菜鳥來說這兒不容易了,我會好好的繼續看代碼,看到吐的~~~

繼續閱讀