精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

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

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

3天內不再提示

Linux進程的睡眠和喚醒

馬哥Linux運維 ? 來源:未知 ? 作者:李倩 ? 2018-03-20 14:34 ? 次閱讀

1 Linux 進程的睡眠和喚醒

在 Linux 中,僅等待 CPU 時間的進程稱為就緒進程,它們被放置在一個運行隊列中,一個就緒進程的狀 態標志位為 TASK_RUNNING。一旦一個運行中的進程時間片用完, Linux 內核的調度器會剝奪這個進程對 CPU 的控制權,并且從運行隊列中選擇一個合適的進程投入運行。

當然,一個進程也可以主動釋放 CPU 的控制權。函數 schedule() 是一個調度函數,它可以被一個進程主動調用,從而調度其它進程占用 CPU。一旦這個主動放棄 CPU 的進程被重新調度占用 CPU,那么它將從上次停止執行的位置開始執行,也就是說它將從調用 schedule() 的下一行代碼處開始執行。

有時候,進程需要等待直到某個特定的事件發生,例如設備初始化完成、I/O 操作完成或定時器到時等。在這種情況下,進程則必須從運行隊列移出,加入到一個等待隊列中,這個時候進程就進入了睡眠狀態。

Linux 中的進程睡眠狀態有兩種:一種是可中斷的睡眠狀態,其狀態標志位

TASK_INTERRUPTIBLE;

另一種是不可中斷 的睡眠狀態,其狀態標志位為 TASK_UNINTERRUPTIBLE。可中斷的睡眠狀態的進程會睡眠直到某個條件變為真,比如說產生一個硬件中斷、釋放 進程正在等待的系統資源或是傳遞一個信號都可以是喚醒進程的條件。不可中斷睡眠狀態與可中斷睡眠狀態類似,但是它有一個例外,那就是把信號傳遞到這種睡眠 狀態的進程不能改變它的狀態,也就是說它不響應信號的喚醒。不可中斷睡眠狀態一般較少用到,但在一些特定情況下這種狀態還是很有用的,比如說:進程必須等 待,不能被中斷,直到某個特定的事件發生。

在現代的 Linux 操作系統中,進程一般都是用調用 schedule() 的方法進入睡眠狀態的,下面的代碼演示了如何讓正在運行的進程進入睡眠狀態。

sleeping_task = current;

set_current_state(TASK_INTERRUPTIBLE);

schedule();

func1();

/* Rest of the code ... */

在第一個語句中,程序存儲了一份進程結構指針 sleeping_task,current 是一個宏,它指向正在執行的進程結構。set_current_state() 將該進程的狀態從執行狀態 TASK_RUNNING 變成睡眠狀態TASK_INTERRUPTIBLE。 如果 schedule() 是被一個狀態為TASK_RUNNING 的進程調度,那么 schedule() 將調度另外一個進程占用 CPU;如果 schedule() 是被一個狀態為 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 的進程調度,那么還有一個附加的步驟將被執行:當前執行的進程在另外一個進程被調度之前會被從運行隊列中移出,這將導致正在運行的那個進程進入睡眠,因為 它已經不在運行隊列中了。

我們可以使用下面的這個函數將剛才那個進入睡眠的進程喚醒。

wake_up_process(sleeping_task);

在調用了 wake_up_process() 以后,這個睡眠進程的狀態會被設置為 TASK_RUNNING,而且調度器會把它加入到運行隊列中去。當然,這個進程只有在下次被調度器調度到的時候才能真正地投入運行。

2 無效喚醒

幾乎在所有的情況下,進程都會在檢查了某些條件之后,發現條件不滿足才進入睡眠。可是有的時候進程卻會在 判定條件為真后開始睡眠,如果這樣的話進程就會無限期地休眠下去,這就是所謂的無效喚醒問題。在操作系統中,當多個進程都企圖對共享數據進行某種處理,而 最后的結果又取決于進程運行的順序時,就會發生競爭條件,這是操作系統中一個典型的問題,無效喚醒恰恰就是由于競爭條件導致的。

設想有兩個進程 A 和 B,A 進程正在處理一個鏈表,它需要檢查這個鏈表是否為空,如果不空就對鏈表里面的數據進行一些操作,同時 B 進程也在往這個鏈表添加節點。當這個鏈表是空的時候,由于無數據可操作,這時 A 進程就進入睡眠,當 B 進程向鏈表里面添加了節點之后它就喚醒 A 進程,其代碼如下:

A 進程:

1 spin_lock(&list_lock);

2if(list_empty(&list_head)) {

3 spin_unlock(&list_lock);

4 set_current_state(TASK_INTERRUPTIBLE);

5 schedule();

6 spin_lock(&list_lock);

7 }

8

9/* Rest of the code ... */

10 spin_unlock(&list_lock);

B 進程:

100 spin_lock(&list_lock);

101 list_add_tail(&list_head, new_node);

102 spin_unlock(&list_lock);

103 wake_up_process(processa_task);

這里會出現一個問題,假如當 A 進程執行到第 3 行后第 4 行前的時候,B 進程被另外一個處理器調度投入運行。在這個時間片內,B 進程執行完了它所有的指令,因此它試圖喚醒 A 進程,而此時的 A 進程還沒有進入睡眠,所以喚醒操作無效。在這之后,A 進程繼續執行,它會錯誤地認為這個時候鏈表仍然是空的,于是將自己的狀態設置為 TASK_INTERRUPTIBLE 然后調用 schedule() 進入睡 眠。由于錯過了 B 進程喚醒,它將會無限期的睡眠下去,這就是無效喚醒問題,因為即使鏈表中有數據需要處理,A 進程也還是睡眠了。

3 避免無效喚醒

如何避免無效喚醒問題呢?我們發現無效喚醒主要發生在檢查條件之后和進程狀態被設置為睡眠狀態之前, 本來 B 進程的 wake_up_process() 提供了一次將 A 進程狀態置為 TASK_RUNNING 的機會,可惜這個時候 A 進程的狀態仍然是 TASK_RUNNING,所以 wake_up_process() 將 A 進程狀態從睡眠狀態轉變為運行狀態的努力 沒有起到預期的作用。要解決這個問題,必須使用一種保障機制使得判斷鏈表為空和設置進程狀態為睡眠狀態成為一個不可分割的步驟才行,也就是必須消除競爭條 件產生的根源,這樣在這之后出現的 wake_up_process () 就可以起到喚醒狀態是睡眠狀態的進程的作用了。找到了原因后,重新設計一下 A 進程的代碼結構,就可以避免上面例子中的無效喚醒問題了。

A 進程:

1 set_current_state(TASK_INTERRUPTIBLE);

2 spin_lock(&list_lock);

3if(list_empty(&list_head)) {

4 spin_unlock(&list_lock);

5 schedule();

6 spin_lock(&list_lock);

7 }

8 set_current_state(TASK_RUNNING);

9

10/* Rest of the code ... */

11 spin_unlock(&list_lock);

可以看到,這段代碼在測試條件之前就將當前執行進程狀態轉設置成 TASK_INTERRUPTIBLE 了,并且在鏈表不為空的情況下又將自己置為 TASK_RUNNING 狀態。這樣一來如果 B 進程在 A 進程進程檢查了鏈表為空以后調用 wake_up_process(),那么 A 進程的狀態就會自動由原來 TASK_INTERRUPTIBLE變成 TASK_RUNNING,此后即使進程又調用了 schedule(),由于它現在的狀態是 TASK_RUNNING,所以仍然不會被從運行隊列中移出,因而不會錯誤的進入睡眠,當然也就避免了無效喚醒問題。

4 Linux 內核的例子

在 Linux 操作系統中,內核的穩定性至關重要,為了避免在 Linux 操作系統內核中出現無效喚醒問題,Linux 內核在需要進程睡眠的時候應該使用類似如下的操作:

/* ‘q’是我們希望睡眠的等待隊列 */

DECLARE_WAITQUEUE(wait,current);

add_wait_queue(q, &wait);

set_current_state(TASK_INTERRUPTIBLE);

/* 或 TASK_INTERRUPTIBLE */

while(!condition) /* ‘condition’ 是等待的條件 */

schedule();

set_current_state(TASK_RUNNING);

remove_wait_queue(q, &wait);

上面的操作,使得進程通過下面的一系列步驟安全地將自己加入到一個等待隊列中進行睡眠:首先調用 DECLARE_WAITQUEUE () 創建一個等待隊列的項,然后調用 add_wait_queue() 把自己加入到等待隊列中,并且將進程的狀態設置為TASK_INTERRUPTIBLE 或者 TASK_INTERRUPTIBLE。然后循環檢查條件是否為真:如果是的話就沒有必要睡眠,如果條件不為真,就調用 schedule()。當進程 檢查的條件滿足后,進程又將自己設置為 TASK_RUNNING 并調用 remove_wait_queue() 將自己移出等待隊列。

從上面可以看到,Linux 的內核代碼維護者也是在進程檢查條件之前就設置進程的狀態為睡眠狀態,然后才循環檢查條件。如果在進程開始睡眠之前條件就已經達成了,那么循環會退出并用 set_current_state() 將自己的狀態設置為就緒,這樣同樣保證了進程不會存在錯誤的進入睡眠的傾向,當然也就不會導致出現無效喚醒問題。

下面讓我們用 linux 內核中的實例來看看 Linux 內核是如何避免無效睡眠的,這段代碼出自 Linux2.6 的內核 (linux-2.6.11/kernel/sched.c: 4254):

4253/* Wait for kthread_stop */

4254 set_current_state(TASK_INTERRUPTIBLE);

4255while (!kthread_should_stop()) {

4256 schedule();

4257 set_current_state(TASK_INTERRUPTIBLE);

4258 }

4259 __set_current_state(TASK_RUNNING);

4260return0;

上面的這些代碼屬于遷移服務線程 migration_thread,這個線程不斷地檢查 kthread_should_stop(),

直 到 kthread_should_stop() 返回 1 它才可以退出循環,也就是說只要 kthread_should_stop() 返回 0 該進程就會一直睡 眠。從代碼中我們可以看出,檢查 kthread_should_stop() 確實是在進程的狀態被置為 TASK_INTERRUPTIBLE 后才開始執行 的。因此,如果在條件檢查之后但是在 schedule() 之前有其他進程試圖喚醒它,那么該進程的喚醒操作不會失效。

小結

通過上面的討論,可以發現在 Linux 中避免進程的無效喚醒的關鍵是在進程檢查條件之前就將進程的狀態置為 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE,并且如果檢查的條件滿足的話就應該將其狀態重新設置為 TASK_RUNNING。這樣無論進程等待的條件是否滿足, 進程都不會因為被移出就緒隊列而錯誤地進入睡眠狀態,從而避免了無效喚醒問題。

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

    關注

    87

    文章

    11123

    瀏覽量

    207908

原文標題:關于 Linux 進程的睡眠和喚醒 ,來看這篇就夠了~

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux進程睡眠喚醒

    Linux中,僅等待CPU時間的進程稱為就緒進程,它們被放置在一個運行隊列中,一個就緒進程的狀 態標志位為 TASK_RUNNING。一旦一個運行中的
    發表于 06-07 12:26 ?391次閱讀

    Linux學習雜談】之進程狀態

    等待態的進程就是進程在等待某些條件,當條件成熟之后可以進入就緒態等待CPU的調度執行。進程位于等待態的情況下如果給了它調度的權限,CPU也是無法執行的。 淺度睡眠等待的時候是可以被信
    發表于 09-27 00:36

    Linux下的進程結構

    進程不但包括程序的指令和數據,而且包括程序計數器和處理器的所有寄存器及存儲臨時數據的進程堆棧,因此正在執行的進程包括處理器當前的一切活動。 因為Linux是一個多
    發表于 05-27 09:24

    如何設置以及喚醒stm32 ucos下睡眠

    請教一下ucos進入睡眠模式,是不是應該先掛起所有任務然后進入睡眠,還是直接進入睡眠等待喚醒(等待喚醒任務是否還在執行調度)?
    發表于 07-29 00:42

    睡眠時的BOR/LPBOR無法喚醒

    。但我的意思是,如果棕色的持續時間很長,那么PIC通常會醒來嗎?棕色是否會導致PIC無法正常喚醒或根本無法喚醒的情況?如果可能的話,那么我會在睡眠時也啟用BOR。我可以在沒有睡眠時啟用
    發表于 10-12 14:46

    求助!關于arduino睡眠喚醒

    我用arduino讀取數據寫了一個睡眠 用看門狗定時喚醒讀取數據但是喚醒后arduino的AI針腳不能讀取求大佬答疑!!
    發表于 07-02 19:06

    Linux進程管理

    Linux進程管理 本章主要介紹進程的概念、狀態、構成以及Linux進程的相關知識。 掌握進程
    發表于 04-28 14:57 ?0次下載

    漸響式睡眠喚醒器電路圖

    漸響式睡眠喚醒器電路圖
    發表于 05-25 13:43 ?1463次閱讀
    漸響式<b class='flag-5'>睡眠</b><b class='flag-5'>喚醒</b>器電路圖

    Linux守護進程詳解

    分享到:標簽:進程控制 Linux 守護進程進程 7.3 Linux守護進程 7.3.1 守
    發表于 10-18 14:24 ?0次下載
    <b class='flag-5'>Linux</b>守護<b class='flag-5'>進程</b>詳解

    如何設置Linux進程睡眠喚醒

    Linux中,僅等待CPU時間的進程稱為就緒進程,它們被放置在一個運行隊列中,一個就緒進程的狀 態標志位為TASK_RUNNING。
    發表于 04-23 14:29 ?892次閱讀

    你知道Linux進程睡眠喚醒操作?

    Linux 中的進程睡眠狀態有兩種:一種是可中斷的睡眠狀態,其狀態標志位TASK_INTERRUPTIBLE;
    發表于 04-23 14:56 ?910次閱讀
    你知道<b class='flag-5'>Linux</b><b class='flag-5'>進程</b>的<b class='flag-5'>睡眠</b>和<b class='flag-5'>喚醒</b>操作?

    GD32低功耗:深度睡眠喚醒系統時鐘變慢問題

    一、問題1、進入深度睡眠后,通過外部中斷喚醒,發現系統時鐘變慢。2、進入休眠模式,通過任何中斷喚醒,系統時鐘正常。二、原因1、從電源管理章節可知,睡眠模式下使用沒有什么需要需要注意的,
    發表于 12-02 15:06 ?21次下載
    GD32低功耗:深度<b class='flag-5'>睡眠</b><b class='flag-5'>喚醒</b>系統時鐘變慢問題

    STM32 低功耗睡眠模式(SLEEP)事件(EVENT)喚醒實現及優化

    STM32 低功耗睡眠模式(SLEEP)事件(EVENT)喚醒實現及優化1. 介紹STM32具有多種低功耗模式,當前以STM32L4系列的低功耗模式最為豐富,此處基于STM32L476
    發表于 12-31 19:08 ?34次下載
    STM32 低功耗<b class='flag-5'>睡眠</b>模式(SLEEP)事件(EVENT)<b class='flag-5'>喚醒</b>實現及優化

    AN010 從深度睡眠模式2喚醒并恢復

    AN010 從深度睡眠模式2喚醒并恢復
    發表于 02-27 18:18 ?1次下載
    AN010 從深度<b class='flag-5'>睡眠</b>模式2<b class='flag-5'>喚醒</b>并恢復

    Linux電源管理的組成與睡眠喚醒

    (Clock)、頻率(Frequency)、電壓(Voltage)、睡眠/喚醒(Suspend/Resume)等方方面面。 Generic PM 軟件架構 Generic PM 主要處理關機、重啟、冬眠
    的頭像 發表于 09-11 15:54 ?447次閱讀
    <b class='flag-5'>Linux</b>電源管理的組成與<b class='flag-5'>睡眠</b><b class='flag-5'>喚醒</b>