溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Linux系統(tǒng)中的內(nèi)核搶占機(jī)制詳解

發(fā)布時(shí)間:2021-09-15 16:42:56 來(lái)源:億速云 閱讀:150 作者:chen 欄目:系統(tǒng)運(yùn)維

本篇內(nèi)容介紹了“Linux系統(tǒng)中的內(nèi)核搶占機(jī)制詳解”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1、內(nèi)核搶占概述

  2.6新的可搶占式內(nèi)核是指內(nèi)核搶占,即當(dāng)進(jìn)程位于內(nèi)核空間時(shí),有一個(gè)更高優(yōu)先級(jí)的任務(wù)出現(xiàn)時(shí),如果當(dāng)前內(nèi)核允許搶占,則可以將當(dāng)前任務(wù)掛起,執(zhí)行優(yōu)先級(jí)更高的進(jìn)程。

  在2.5.4版本之前,Linux內(nèi)核是不可搶占的,高優(yōu)先級(jí)的進(jìn)程不能中止正在內(nèi)核中運(yùn)行的低優(yōu)先級(jí)的進(jìn)程而搶占CPU運(yùn)行。進(jìn)程一旦處于核心態(tài)(例如用戶進(jìn)程執(zhí)行系統(tǒng)調(diào)用),則除非進(jìn)程自愿放棄CPU,否則該進(jìn)程將一直運(yùn)行下去,直至完成或退出內(nèi)核。與此相反,一個(gè)可搶占的Linux內(nèi)核可以讓Linux內(nèi)核如同用戶空間一樣允許被搶占。當(dāng)一個(gè)高優(yōu)先級(jí)的進(jìn)程到達(dá)時(shí),不管當(dāng)前進(jìn)程處于用戶態(tài)還是核心態(tài),如果當(dāng)前允許搶占,可搶占內(nèi)核的Linux都會(huì)調(diào)度高優(yōu)先級(jí)的進(jìn)程運(yùn)行。

2、用戶搶占

  內(nèi)核即將返回用戶空間的時(shí)候,如果need resched標(biāo)志被設(shè)置,會(huì)導(dǎo)致schedule()被調(diào)用,此時(shí)就會(huì)發(fā)生用戶搶占。在內(nèi)核返回用戶空間的時(shí)候,它知道自己是安全的。所以,內(nèi)核無(wú)論是在從中斷處理程序還是在系統(tǒng)調(diào)用后返回,都會(huì)檢查need resched標(biāo)志。如果它被設(shè)置了,那么,內(nèi)核會(huì)選擇一個(gè)其他(更合適的)進(jìn)程投入運(yùn)行。

  簡(jiǎn)而言之,用戶搶占在以下情況時(shí)產(chǎn)生:

  從系統(tǒng)調(diào)返回用戶空間。

  從中斷處理程序返回用戶空間。

3、不可搶占內(nèi)核的特點(diǎn)

  在不支持內(nèi)核搶占的內(nèi)核中,內(nèi)核代碼可以一直執(zhí)行,到它完成為止。也就是說(shuō),調(diào)度程序沒(méi)有辦法在一個(gè)內(nèi)核級(jí)的任務(wù)正在執(zhí)行的時(shí)候重新調(diào)度—內(nèi)核中的各任務(wù)是協(xié)作方式調(diào)度的,不具備搶占性。當(dāng)然,運(yùn)行于內(nèi)核態(tài) 的進(jìn)程可以主動(dòng)放棄CPU,比如,在系統(tǒng)調(diào)用服務(wù)例程中,由于內(nèi)核代碼由于等待資源而放棄CPU,這種情況叫做計(jì)劃性進(jìn)程切換(planned process switch)。內(nèi)核代碼一直要執(zhí)行到完成(返回用戶空間)或明顯的阻塞為止,

  在單CPU情況下,這樣的設(shè)定大大簡(jiǎn)化了內(nèi)核的同步和保護(hù)機(jī)制。可以分兩步對(duì)此加以分析:

  首先,不考慮進(jìn)程在內(nèi)核中自愿放棄CPU的情況(也即在內(nèi)核中不發(fā)生進(jìn)程的切換)。一個(gè)進(jìn)程一旦進(jìn)入內(nèi)核就將一直運(yùn)行下去,直到完成或退出內(nèi)核。在其沒(méi)有完成或退出內(nèi)核之前,不會(huì)有另外一個(gè)進(jìn)程進(jìn)入內(nèi)核,即進(jìn)程在內(nèi)核中的執(zhí)行是串行的,不可能有多個(gè)進(jìn)程同時(shí)在內(nèi)核中運(yùn)行,這樣內(nèi)核代碼設(shè)計(jì)時(shí)就不用考慮多個(gè)進(jìn)程同時(shí)執(zhí)行所帶來(lái)的并發(fā)問(wèn)題。Linux的內(nèi)核開(kāi)發(fā)人員就不用考慮復(fù)雜的進(jìn)程并發(fā)執(zhí)行互斥訪問(wèn)臨界資源的問(wèn)題。當(dāng)進(jìn)程在訪問(wèn)、修改內(nèi)核的數(shù)據(jù)結(jié)構(gòu)時(shí)就不需要加鎖來(lái)防止多個(gè)進(jìn)程同時(shí)進(jìn)入臨界區(qū)。這時(shí)只需再考慮一下中斷的情況,若有中斷處理例程也有可能訪問(wèn)進(jìn)程正在訪問(wèn)的數(shù)據(jù)結(jié)構(gòu),那么進(jìn)程只要在進(jìn)入臨界區(qū)前先進(jìn)行關(guān)中斷操作,退出臨界區(qū)時(shí)進(jìn)行開(kāi)中斷操作就可以了。

  再考慮一下進(jìn)程自愿放棄CPU的情況。因?yàn)閷?duì)CPU的放棄是自愿的、主動(dòng)的,也就意味著進(jìn)程在內(nèi)核中的切換是預(yù)先知道的,不會(huì)出現(xiàn)在不知道的情況下發(fā)生進(jìn)程的切換。這樣就只需在發(fā)生進(jìn)程切換的地方考慮一下多個(gè)進(jìn)程同時(shí)執(zhí)行所可能帶來(lái)的并發(fā)問(wèn)題,而不必在整個(gè)內(nèi)核范圍內(nèi)都要考慮進(jìn)程并發(fā)執(zhí)行問(wèn)題。

4、為什么需要內(nèi)核搶占?

  實(shí)現(xiàn)內(nèi)核的可搶占對(duì)Linux具有重要意義。首先,這是將Linux應(yīng)用于實(shí)時(shí)系統(tǒng)所必需的。實(shí)時(shí)系統(tǒng)對(duì)響應(yīng)時(shí)間有嚴(yán)格的限定,當(dāng)一個(gè)實(shí)時(shí)進(jìn)程被實(shí)時(shí)設(shè)備的硬件中斷喚醒后,它應(yīng)在限定的時(shí)間內(nèi)被調(diào)度執(zhí)行。而Linux不能滿足這一要求,因?yàn)長(zhǎng)inux的內(nèi)核是不可搶占的,不能確定系統(tǒng)在內(nèi)核中的停留時(shí)間。事實(shí)上當(dāng)內(nèi)核執(zhí)行長(zhǎng)的系統(tǒng)調(diào)用時(shí),實(shí)時(shí)進(jìn)程要等到內(nèi)核中運(yùn)行的進(jìn)程退出內(nèi)核才能被調(diào)度,由此產(chǎn)生的響應(yīng)延遲,在如今的硬件條件下,會(huì)長(zhǎng)達(dá)100ms級(jí)。

  這對(duì)于那些要求高實(shí)時(shí)響應(yīng)的系統(tǒng)是不能接受的。而可搶占的內(nèi)核不僅對(duì)Linux的實(shí)時(shí)應(yīng)用至關(guān)重要,而且能解決Linux對(duì)多媒體(video, audio)等要求低延遲的應(yīng)用支持不夠好的缺陷。

  由于可搶占內(nèi)核的重要性,在Linux2.5.4版本發(fā)布時(shí),可搶占被并入內(nèi)核,同SMP一樣作為內(nèi)核的一項(xiàng)標(biāo)準(zhǔn)可選配置。

5、什么情況不允許內(nèi)核搶占

  有幾種情況Linux內(nèi)核不應(yīng)該被搶占,除此之外Linux內(nèi)核在任意一點(diǎn)都可被搶占。這幾種情況是:

  內(nèi)核正進(jìn)行中斷處理。在Linux內(nèi)核中進(jìn)程不能搶占中斷(中斷只能被其他中斷中止、搶占,進(jìn)程不能中止、搶占中斷),在中斷例程中不允許進(jìn)行進(jìn)程調(diào)度。進(jìn)程調(diào)度函數(shù)schedule()會(huì)對(duì)此作出判斷,如果是在中斷中調(diào)用,會(huì)打印出錯(cuò)信息。

  內(nèi)核正在進(jìn)行中斷上下文的Bottom Half(中斷的底半部)處理。硬件中斷返回前會(huì)執(zhí)行軟中斷,此時(shí)仍然處于中斷上下文中。

    內(nèi)核的代碼段正持有spinlock自旋鎖、writelock/readlock讀寫鎖等鎖,處干這些鎖的保護(hù)狀態(tài)中。內(nèi)核中的這些鎖是為了在SMP系統(tǒng)中短時(shí)間內(nèi)保證不同CPU上運(yùn)行的進(jìn)程并發(fā)執(zhí)行的正確性。當(dāng)持有這些鎖時(shí),內(nèi)核不應(yīng)該被搶占,否則由于搶占將導(dǎo)致其他CPU長(zhǎng)期不能獲得鎖而死等。

  內(nèi)核正在執(zhí)行調(diào)度程序Scheduler。搶占的原因就是為了進(jìn)行新的調(diào)度,沒(méi)有理由將調(diào)度程序搶占掉再運(yùn)行調(diào)度程序。

  內(nèi)核正在對(duì)每個(gè)CPU“私有”的數(shù)據(jù)結(jié)構(gòu)操作(Per-CPU date structures)。在SMP中,對(duì)于per-CPU數(shù)據(jù)結(jié)構(gòu)未用spinlocks保護(hù),因?yàn)檫@些數(shù)據(jù)結(jié)構(gòu)隱含地被保護(hù)了(不同的CPU有不一樣的per-CPU數(shù)據(jù),其他CPU上運(yùn)行的進(jìn)程不會(huì)用到另一個(gè)CPU的per-CPU數(shù)據(jù))。但是如果允許搶占,但一個(gè)進(jìn)程被搶占后重新調(diào)度,有可能調(diào)度到其他的CPU上去,這時(shí)定義的Per-CPU變量就會(huì)有問(wèn)題,這時(shí)應(yīng)禁搶占。

  為保證Linux內(nèi)核在以上情況下不會(huì)被搶占,搶占式內(nèi)核使用了一個(gè)變量preempt_ count,稱為內(nèi)核搶占鎖。這一變量被設(shè)置在進(jìn)程的PCB結(jié)構(gòu)task_struct中。每當(dāng)內(nèi)核要進(jìn)入以上幾種狀態(tài)時(shí),變量preempt_ count就加1,指示內(nèi)核不允許搶占。每當(dāng)內(nèi)核從以上幾種狀態(tài)退出時(shí),變量preempt_ count就減1,同時(shí)進(jìn)行可搶占的判斷與調(diào)度。

  從中斷返回內(nèi)核空間的時(shí)候,內(nèi)核會(huì)檢查need_resched和preempt_count的值。如果need_ resched被設(shè)置,并且preempt count為0的話,這說(shuō)明可能有一個(gè)更為重要的任務(wù)需要執(zhí)行并且可以安全地?fù)屨?,此時(shí),調(diào)度程序就會(huì)被調(diào)用。如果preempt-count不為0,則說(shuō)明內(nèi)核現(xiàn)在處干不可搶占狀態(tài),不能進(jìn)行重新調(diào)度。這時(shí),就會(huì)像通常那樣直接從中斷返回當(dāng)前執(zhí)行進(jìn)程。如果當(dāng)前進(jìn)程持有的所有的鎖都被釋放了,那么preempt_ count就會(huì)重新為0。此時(shí),釋放鎖的代碼會(huì)檢查need_ resched是否被設(shè)置。如果是的話,就會(huì)調(diào)用調(diào)度程序。

6、內(nèi)核搶占時(shí)機(jī)

  在2.6版的內(nèi)核中,內(nèi)核引入了搶占能力;現(xiàn)在,只要重新調(diào)度是安全的,那么內(nèi)核就可以在任何時(shí)間搶占正在執(zhí)行的任務(wù)。

  那么,什么時(shí)候重新調(diào)度才是安全的呢?只要premptcount為0,內(nèi)核就可以進(jìn)行搶占。通常鎖和中斷是非搶占區(qū)域的標(biāo)志。由于內(nèi)核是支持SMP的,所以,如果沒(méi)有持有鎖,那么正在執(zhí)行的代碼就是可重新導(dǎo)人的,也就是可以搶占的。

  如果內(nèi)核中的進(jìn)程被阻塞了,或它顯式地調(diào)用了schedule(),內(nèi)核搶占也會(huì)顯式地發(fā)生。這種形式的內(nèi)核搶占從來(lái)都是受支持的(實(shí)際上是主動(dòng)讓出CPU),因?yàn)楦緹o(wú)需額外的邏輯來(lái)保證內(nèi)核可以安全地被搶占。如果代碼顯式的調(diào)用了schedule(),那么它應(yīng)該清楚自己是可以安全地被搶占的。

  內(nèi)核搶占可能發(fā)生在:

  當(dāng)從中斷處理程序正在執(zhí)行,且返回內(nèi)核空間之前。

  當(dāng)內(nèi)核代碼再一次具有可搶占性的時(shí)候,如解鎖及使能軟中斷等。

  如果內(nèi)核中的任務(wù)顯式的調(diào)用schedule()

  如果內(nèi)核中的任務(wù)阻塞(這同樣也會(huì)導(dǎo)致調(diào)用schedule())

7、如何支持搶占內(nèi)核

  搶占式Linux內(nèi)核的修改主要有兩點(diǎn):一是對(duì)中斷的入口代碼和返回代碼進(jìn)行修改。在中斷的入口內(nèi)核搶占鎖preempt_count加1,以禁止內(nèi)核搶占;在中斷的返回處,內(nèi)核搶占鎖preempt_count減1,使內(nèi)核有可能被搶占。

  我們說(shuō)可搶占Linux內(nèi)核在內(nèi)核的任一點(diǎn)可被搶占,主要就是因?yàn)樵谌我庖稽c(diǎn)中斷都有可能發(fā)生,每當(dāng)中斷發(fā)生,Linux可搶占內(nèi)核在處理完中斷返回時(shí)都會(huì)進(jìn)行內(nèi)核的可搶占判斷。若內(nèi)核當(dāng)前所處狀態(tài)允許被搶占,內(nèi)核都會(huì)重新進(jìn)行調(diào)度選取高優(yōu)先級(jí)的進(jìn)程運(yùn)行。這一點(diǎn)是與非可搶占的內(nèi)核不一樣的。在非可搶占的Linux內(nèi)核中,從硬件中斷返回時(shí),只有當(dāng)前被中斷進(jìn)程是用戶態(tài)進(jìn)程時(shí)才會(huì)重新調(diào)度,若當(dāng)前被中斷進(jìn)程是核心態(tài)進(jìn)程,則不進(jìn)行調(diào)度,而是恢復(fù)被中斷的進(jìn)程繼續(xù)運(yùn)行。

  另一基本修改是重新定義了自旋鎖、讀、寫鎖,在鎖操作時(shí)增加了對(duì)preempt count變量的操作。在對(duì)這些鎖進(jìn)行加鎖操作時(shí)preemptcount變量加1,以禁止內(nèi)核搶占;在釋放鎖時(shí)preemptcount變量減1,并在內(nèi)核的搶占條件滿足且需要重新調(diào)度時(shí)進(jìn)行搶占調(diào)度。

另外一種可搶占內(nèi)核實(shí)現(xiàn)方案是在內(nèi)核代碼段中插入搶占點(diǎn)(preemption point)的方案。在這一方案中,首先要找出內(nèi)核中產(chǎn)生長(zhǎng)延遲的代碼段,然后在這一內(nèi)核代碼段的適當(dāng)位置插入搶占點(diǎn),使得系統(tǒng)不必等到這段代碼執(zhí)行完就可重新調(diào)度。這樣對(duì)于需要快速響應(yīng)的事件,系統(tǒng)就可以盡快地將服務(wù)進(jìn)程調(diào)度到CPU運(yùn)行。搶占點(diǎn)實(shí)際上是對(duì)進(jìn)程調(diào)度函數(shù)的調(diào)用,代碼如下:

代碼如下:


if(current->need_resched)schedule();</p> <p>if(current->need_resched)schedule();


  通常這樣的代碼段是一個(gè)循環(huán)體,插入搶占點(diǎn)的方案就是在這一循環(huán)體中不斷檢測(cè)need_ resched的值,在必要的時(shí)候調(diào)用schedule()令當(dāng)前進(jìn)程強(qiáng)行放棄CPU

8、何時(shí)需要重新調(diào)度

  內(nèi)核必須知道在什么時(shí)候調(diào)用schedule()。如果僅靠用戶程序代碼顯式地調(diào)用schedule(),它們可能就會(huì)永遠(yuǎn)地執(zhí)行下去。相反,內(nèi)核提供了一個(gè)need_resched標(biāo)志來(lái)表明是否需要重新執(zhí)行一次調(diào)度。當(dāng)某個(gè)進(jìn)程耗盡它的時(shí)間片時(shí),scheduler tick()就會(huì)設(shè)置這個(gè)標(biāo)志;當(dāng)一個(gè)優(yōu)先級(jí)高的進(jìn)程進(jìn)入可執(zhí)行狀態(tài)的時(shí)候,try_to_wake_up也會(huì)設(shè)置這個(gè)標(biāo)志。

  set_ tsk_need_resched:設(shè)置指定進(jìn)程中的need_ resched標(biāo)志

  clear tsk need_resched:清除指定進(jìn)程中的need_ resched標(biāo)志

  need_resched():檢查need_ resched標(biāo)志的值;如果被設(shè)置就返回真,否則返回假

  信號(hào)量、等到隊(duì)列、completion等機(jī)制喚醒時(shí)都是基于waitqueue的,而waitqueue的喚醒函數(shù)為default_wake_function,其調(diào)用try_to_wake_up將進(jìn)程更改為可運(yùn)行狀態(tài)并置待調(diào)度標(biāo)志。

  在返回用戶空間以及從中斷返回的時(shí)候,內(nèi)核也會(huì)檢查need_resched標(biāo)志。如果已被設(shè)置,內(nèi)核會(huì)在繼續(xù)執(zhí)行之前調(diào)用調(diào)度程序。

  每個(gè)進(jìn)程都包含一個(gè)need_resched標(biāo)志,這是因?yàn)樵L問(wèn)進(jìn)程描述符內(nèi)的數(shù)值要比訪問(wèn)一個(gè)全局變量快(因?yàn)閏urrent宏速度很快并且描述符通常都在高速緩存中)。在2.2以前的內(nèi)核版本中,該標(biāo)志曾經(jīng)是一個(gè)全局變量。2.2到2.4版內(nèi)核中它在task_struct中。而在2.6版中,它被移到thread_info結(jié)構(gòu)體里,用一個(gè)特別的標(biāo)志變量中的一位來(lái)表示??梢?jiàn),內(nèi)核開(kāi)發(fā)者總是在不斷改進(jìn)。

9、避免內(nèi)核搶占
進(jìn)程一旦調(diào)用了schedule,如果再次被調(diào)度運(yùn)行,那么有下面幾種可能:1.狀態(tài)為TASK_RUNNING,處于運(yùn)行隊(duì)列,那么它肯定有機(jī)會(huì)再運(yùn)行;2.處于睡眠隊(duì)列,那么一旦條件滿足被喚醒,那么它就會(huì)運(yùn)行。那么如果一個(gè)進(jìn)程被搶占的話,而且它不在運(yùn)行隊(duì)列,那么怎么再讓它運(yùn)行呢?答案是它不能運(yùn)行了。為了避免這種情況,就必須避免處于非TASK_RUNNING的進(jìn)程被搶占的進(jìn)程不被趕出運(yùn)行隊(duì)列,也就是下面的代碼,schedule的代碼:

代碼如下:


if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {</p> <p>switch_count = &prev->nvcsw;</p> <p>if (unlikely((prev->state & TASK_INTERRUPTIBLE) && unlikely(signal_pending(prev))))</p> <p>prev->state = TASK_RUNNING;</p> <p>else {</p> <p>if (prev->state == TASK_UNINTERRUPTIBLE)</p> <p>rq->nr_uninterruptible++;</p> <p>deactivate_task(prev, rq);</p> <p>}

也許有人會(huì)問(wèn),怎么會(huì)有不是TASK_RUNNING的進(jìn)程而且被搶占的,這個(gè)問(wèn)題實(shí)在難以回答,可是記住,進(jìn)程狀態(tài)和其所在的隊(duì)列沒(méi)有關(guān)系,設(shè)置進(jìn)程狀態(tài)和搶占總是有可能有間隙的。我們看看下面的代碼:

代碼如下:


for (;;) { \</p> <p>1: prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \</p> <p>2: if (condition) \</p> <p>3: break; \</p> <p>4: schedule(); \</p> <p>}

如果在1中被搶占,恰恰在設(shè)置完進(jìn)程為TASK_UNINTERRUPTIBLE的時(shí)候被搶占,本來(lái)馬上就要測(cè)試條件是否滿足了,結(jié)果又被加入睡眠隊(duì)列去睡眠了,如果沒(méi)有PREEMPT_ACTIVE,那么在schedule中就會(huì)被移出運(yùn)行隊(duì)列,如果只有這一次喚醒機(jī)會(huì),那么就永遠(yuǎn)喚不醒這個(gè)進(jìn)程了,如果本次從schedule回來(lái)?xiàng)l件不滿足,那么在下面的schedue中就會(huì)被移出運(yùn)行隊(duì)列,這不是搶占的職責(zé),如果非要怎么做就會(huì)出錯(cuò),在dequeue_task中由array->queue已經(jīng)為空了,在第二次真正出隊(duì)的時(shí)候就會(huì)由于空指針引用而出錯(cuò)(這其實(shí)不會(huì)發(fā)生,因?yàn)橹灰獜膕chedue回來(lái),進(jìn)程的狀態(tài)肯定是TASK_RUNNING,僅僅是一個(gè)例子)。因此必須保證在將進(jìn)程從運(yùn)行隊(duì)列移除的時(shí)候,它必須在運(yùn)行隊(duì)列,否則移個(gè)鳥(niǎo)??!實(shí)際上PREEMPT_ACTIVE的作用就是防止將處于非TASK_RUNNING狀態(tài)的進(jìn)程并且沒(méi)有在任何睡眠隊(duì)列的進(jìn)程移出運(yùn)行隊(duì)列,總之必須保證進(jìn)程在一個(gè)隊(duì)列中或者可以被喚醒,被搶占的進(jìn)程是不能被喚醒的,如果它還不在運(yùn)行隊(duì)列中,那么它將永遠(yuǎn)不能再運(yùn)行了。那么PREEMPT_ACTIVE是怎么保證被搶占的進(jìn)程不會(huì)被移除運(yùn)行隊(duì)列呢?就是在preempt_schedule實(shí)現(xiàn)的:

代碼如下:


asmlinkage void __sched preempt_schedule(void)</p> <p>{</p> <p>struct thread_info *ti = current_thread_info();</p> <p>if (likely(ti->preempt_count || irqs_disabled()))</p> <p>return;</p> <p>do {</p> <p>add_preempt_count(PREEMPT_ACTIVE); //設(shè)置PREEMPT_ACTIVE位,一直到下面的sub_preempt_count(PREEMPT_ACTIVE),這期間不能再搶占這個(gè)進(jìn)程,不過(guò)再搶占也沒(méi)有意義,如果非要搶占,出了下面的sub_preempt_count(PREEMPT_ACTIVE)也不遲</p> <p>schedule();</p> <p>sub_preempt_count(PREEMPT_ACTIVE); //搶占完畢后清除之</p> <p>barrier();</p> <p>} while (unlikely(test_thread_flag(TIF_NEED_RESCHED)));</p> <p>}

除了這里之外,在早一些的內(nèi)核中從中斷返回內(nèi)核空間時(shí)如果要搶占,在entery.S中也會(huì)加上這個(gè)這個(gè)PREEMPT_ACTIVE?,F(xiàn)在還有一個(gè)問(wèn)題,就是為何wait_event要用那種實(shí)現(xiàn)方式呢?為何需要一個(gè)循環(huán)呢?我的回答就是:這種情況下進(jìn)程之所以能被喚醒就是因?yàn)樗尤肓艘粋€(gè)睡眠隊(duì)列,如果如你所說(shuō)在schedule之后直接判斷condition的話是不安全的,因?yàn)閱拘巡灰欢ㄊ且驗(yàn)闂l件滿足了,萬(wàn)一兩個(gè)進(jìn)程同時(shí)被喚醒那很可能有一個(gè)進(jìn)程條件不能滿足,如果正好此時(shí)進(jìn)程被搶占,那么這個(gè)進(jìn)程就沒(méi)有機(jī)會(huì)加入睡眠隊(duì)列了,也就沒(méi)有機(jī)會(huì)被喚醒了,雖然PREEMPT_ACTIVE保證了這個(gè)進(jìn)程不出運(yùn)行隊(duì)列,但是卻失去了程序的本意,程序的本意是通過(guò)喚醒運(yùn)行隊(duì)列來(lái)使進(jìn)程運(yùn)行,而此時(shí)卻成了完全依據(jù)優(yōu)先級(jí)了,即使條件滿足因?yàn)檫@個(gè)進(jìn)程不在睡眠隊(duì)列也不會(huì)被喚醒,系統(tǒng)就亂掉了。

其實(shí)很簡(jiǎn)單,必須在將進(jìn)程加入到睡眠隊(duì)列以后再判斷條件,因?yàn)檫@樣可以不漏掉喚醒通知,如果反過(guò)來(lái)的話,就是先判斷再加入睡眠隊(duì)列,如果在加入之前其它進(jìn)程喚醒了這個(gè)睡眠隊(duì)列,那么這個(gè)進(jìn)程就會(huì)漏掉這次喚醒,之所以會(huì)有一個(gè)循環(huán)是因?yàn)榭赡懿恢挂粋€(gè)進(jìn)程被喚醒,那么就會(huì)出現(xiàn)競(jìng)爭(zhēng),這個(gè)循環(huán)就是為了競(jìng)爭(zhēng)而設(shè)置的,這個(gè)循環(huán)保證了每個(gè)出了這個(gè)循環(huán)的進(jìn)程都能安全帶著結(jié)果為真的條件。

另外,說(shuō)到TASK_RUNNING這個(gè)狀態(tài),又有人問(wèn)了,為何在缺頁(yè)中要把進(jìn)程狀態(tài)設(shè)置為TASK_RUNNING,難道缺頁(yè)前不是TASK_RUNNING嗎?大部分情況下應(yīng)該是,可是linux內(nèi)核不敢保證,之所以在handle_mm_fault中將進(jìn)程狀態(tài)設(shè)置為TASK_RUNNING是為了保證在缺頁(yè)處理中如果睡眠,那么進(jìn)程可以被喚醒,舉個(gè)例子,在select中,當(dāng)進(jìn)程被設(shè)置為非TASK_RUNNING之后還會(huì)copy_from_user,而這卻可能引起缺頁(yè)。如果不把進(jìn)程狀態(tài)設(shè)置為TASK_RUNNING,那么萬(wàn)一在page fault中schedule了,那么這個(gè)進(jìn)程就會(huì)被趕出運(yùn)行隊(duì)列,就再也回不來(lái)了,為了預(yù)防之,措施是:在任何調(diào)用schedule的地方分辨狀態(tài),然后設(shè)置進(jìn)程狀態(tài),比如前面說(shuō)的用PREEMPT_ACTIVE來(lái)預(yù)防,另外就是像handle_mm_fault中做到的一樣,盡量使進(jìn)程在TASK_RUNNABE狀態(tài)下進(jìn)入schedule。不過(guò)我想是不是現(xiàn)在這個(gè)應(yīng)該去掉了,即使在缺頁(yè)中不把進(jìn)程設(shè)置為運(yùn)行態(tài),如果非要調(diào)度,也在之前設(shè)為運(yùn)行臺(tái)了。

ACTIVE_PREEMPT的作用:防止已經(jīng)處于非運(yùn)行態(tài)的進(jìn)程還沒(méi)有加入睡眠隊(duì)列的時(shí)候就被搶占然后剔除出運(yùn)行隊(duì)列。這樣就永遠(yuǎn)也回不來(lái)了,雖然這種情況很少見(jiàn),一般都是先將進(jìn)程放到睡眠隊(duì)列再設(shè)置狀態(tài)。

“Linux系統(tǒng)中的內(nèi)核搶占機(jī)制詳解”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI