




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第第頁Linux進(jìn)程的睡眠和喚醒(Linux)進(jìn)程的睡眠和喚醒
在Linux中,僅等待(CPU)時間的進(jìn)程稱為就緒進(jìn)程,它們被放置在一個運(yùn)行隊(duì)列中,一個就緒進(jìn)程的狀態(tài)標(biāo)志位為
TASK_RUNNING。一旦一個運(yùn)行中的進(jìn)程時間片用完,Linux內(nèi)核的調(diào)度器會剝奪這個進(jìn)程對CPU的控制權(quán),并且從運(yùn)行隊(duì)列中選擇一個合適的進(jìn)程投入運(yùn)行。
當(dāng)然,一個進(jìn)程也可以主動釋放CPU的控制權(quán)。函數(shù)
schedule()
是一個調(diào)度函數(shù),它可以被一個進(jìn)程主動調(diào)用,從而調(diào)度其它進(jìn)程占用CPU。一旦這個主動放棄CPU的進(jìn)程被重新調(diào)度占用CPU,那么它將從上次停止執(zhí)行的位置開始執(zhí)行,也就是說它將從調(diào)用
schedule()
的下一行代碼處開始執(zhí)行。
有時候,進(jìn)程需要等待直到某個特定的事件發(fā)生,例如設(shè)備初始化完成、I/O操作完成或(定時器)到時等。在這種情況下,進(jìn)程則必須從運(yùn)行隊(duì)列移出,加入到一個等待隊(duì)列中,這個時候進(jìn)程就進(jìn)入了睡眠狀態(tài)。
Linux中的進(jìn)程睡眠狀態(tài)有兩種:
一種是可中斷的睡眠狀態(tài),其狀態(tài)標(biāo)志位TASK_INTERRUP(TI)BLE。
另一種是不可中斷的睡眠狀態(tài),其狀態(tài)標(biāo)志位為TASK_UNINTERRUPTIBLE。
可中斷的睡眠狀態(tài)的進(jìn)程會睡眠直到某個條件變?yōu)檎妫热缯f產(chǎn)生一個(硬件)中斷、釋放進(jìn)程正在等待的系統(tǒng)資源或是傳遞一個(信號)都可以是喚醒進(jìn)程的條件。不可中斷睡眠狀態(tài)與可中斷睡眠狀態(tài)類似,但是它有一個例外,那就是把信號傳遞到這種睡眠狀態(tài)的進(jìn)程不能改變它的狀態(tài),也就是說它不響應(yīng)信號的喚醒。不可中斷睡眠狀態(tài)一般較少用到,但在一些特定情況下這種狀態(tài)還是很有用的,比如說:進(jìn)程必須等待,不能被中斷,直到某個特定的事件發(fā)生。
在現(xiàn)代的Linux(操作系統(tǒng))中,進(jìn)程一般都是用調(diào)用
schedule()
的方法進(jìn)入睡眠狀態(tài)的,下面的代碼演示了如何讓正在運(yùn)行的進(jìn)程進(jìn)入睡眠狀態(tài)。
sleeping_task
=
current;set_current_state(TASK_INTERRUPTIBLE);schedule();func1();/*
Rest
of
the
code
...
*/
在第一個語句中,程序存儲了一份進(jìn)程結(jié)構(gòu)指針
sleeping_task,current
是一個宏,它指向正在執(zhí)行的進(jìn)程結(jié)構(gòu)。
set_current_state()
將該進(jìn)程的狀態(tài)從執(zhí)行狀態(tài)
TASK_RUNNING
變成睡眠狀態(tài)
TASK_INTERRUPTIBLE。如果
schedule()
是被一個狀態(tài)為
TASK_RUNNING
的進(jìn)程調(diào)度,那么
schedule()
將調(diào)度另外一個進(jìn)程占用CPU。
如果
schedule()
是被一個狀態(tài)為
TASK_INTERRUPTIBLE
或
TASK_UNINTERRUPTIBLE
的進(jìn)程調(diào)度,那么還有一個附加的步驟將被執(zhí)行:當(dāng)前執(zhí)行的進(jìn)程在另外一個進(jìn)程被調(diào)度之前會被從運(yùn)行隊(duì)列中移出,這將導(dǎo)致正在運(yùn)行的那個進(jìn)程進(jìn)入睡眠,因?yàn)樗呀?jīng)不在運(yùn)行隊(duì)列中了。
我們可以使用下面的這個函數(shù)將剛才那個進(jìn)入睡眠的進(jìn)程喚醒。
wake_up_process(sleeping_task);
在調(diào)用了
wake_up_process()
以后,這個睡眠進(jìn)程的狀態(tài)會被設(shè)置為
TASK_RUNNING,而且調(diào)度器會把它加入到運(yùn)行隊(duì)列中去。當(dāng)然,這個進(jìn)程只有在下次被調(diào)度器調(diào)度到的時候才能真正地投入運(yùn)行。
無效喚醒
幾乎在所有的情況下,進(jìn)程都會在檢查了某些條件之后,發(fā)現(xiàn)條件不滿足才進(jìn)入睡眠??墒怯械臅r候進(jìn)程卻會在判定條件為真后開始睡眠,如果這樣的話進(jìn)程就會無限期地休眠下去,這就是所謂的無效喚醒問題。
在操作系統(tǒng)中,當(dāng)多個進(jìn)程都企圖對共享數(shù)據(jù)進(jìn)行某種處理,而最后的結(jié)果又取決于進(jìn)程運(yùn)行的順序時,就會發(fā)生競爭條件,這是操作系統(tǒng)中一個典型的問題,無效喚醒恰恰就是由于競爭條件導(dǎo)致的。
設(shè)想有兩個進(jìn)程A和B,A進(jìn)程正在處理一個鏈表,它需要檢查這個鏈表是否為空,如果不空就對鏈表里面的數(shù)據(jù)進(jìn)行一些操作,同時B進(jìn)程也在往這個鏈表添加節(jié)點(diǎn)。當(dāng)這個鏈表是空的時候,由于無數(shù)據(jù)可操作,這時A進(jìn)程就進(jìn)入睡眠,當(dāng)B進(jìn)程向鏈表里面添加了節(jié)點(diǎn)之后它就喚醒A進(jìn)程,其代碼如下:
A進(jìn)程:
1
s(pi)n_lock(2
if
(list_empty(4
set_current_state(TASK_INTERRUPTIBLE);5
schedule();6
spin_lock(7
}89
/*
Rest
of
the
code
...
*/10
spin_unlock(
B進(jìn)程:
100
spin_lock(101
list_add_t(ai)l(102
spin_unlock(103
wake_up_process(processa_task);
這里會出現(xiàn)一個問題,假如當(dāng)A進(jìn)程執(zhí)行到第3行后第4行前的時候,B進(jìn)程被另外一個處理器調(diào)度投入運(yùn)行。在這個時間片內(nèi),B進(jìn)程執(zhí)行完了它所有的指令,因此它試圖喚醒A進(jìn)程,而此時的A進(jìn)程還沒有進(jìn)入睡眠,所以喚醒操作無效。
在這之后,A進(jìn)程繼續(xù)執(zhí)行,它會錯誤地認(rèn)為這個時候鏈表仍然是空的,于是將自己的狀態(tài)設(shè)置為
TASK_INTERRUPTIBLE
然后調(diào)用
schedule()
進(jìn)入睡眠。由于錯過了B進(jìn)程喚醒,它將會無限期的睡眠下去,這就是無效喚醒問題,因?yàn)榧词规湵碇杏袛?shù)據(jù)需要處理,A進(jìn)程也還是睡眠了。
避免無效喚醒
如何避免無效喚醒問題呢?
我們發(fā)現(xiàn)無效喚醒主要發(fā)生在檢查條件之后和進(jìn)程狀態(tài)被設(shè)置為睡眠狀態(tài)之前,本來B進(jìn)程的
wake_up_process()
提供了一次將A進(jìn)程狀態(tài)置為
TASK_RUNNING
的機(jī)會,可惜這個時候A進(jìn)程的狀態(tài)仍然是
TASK_RUNNING,所以
wake_up_process()
將A進(jìn)程狀態(tài)從睡眠狀態(tài)轉(zhuǎn)變?yōu)檫\(yùn)行狀態(tài)的努力沒有起到預(yù)期的作用。
要解決這個問題,必須使用一種保障機(jī)制使得判斷鏈表為空和設(shè)置進(jìn)程狀態(tài)為睡眠狀態(tài)成為一個不可分割的步驟才行,也就是必須消除競爭條件產(chǎn)生的根源,這樣在這之后出現(xiàn)的
wake_up_process()
就可以起到喚醒狀態(tài)是睡眠狀態(tài)的進(jìn)程的作用了。
找到了原因后,重新設(shè)計(jì)一下A進(jìn)程的代碼結(jié)構(gòu),就可以避免上面例子中的無效喚醒問題了。
A進(jìn)程:
1
set_current_state(TASK_INTERRUPTIBLE);2
spin_lock(3
if
(list_empty(5
schedule();6
spin_lock(7
}8
set_current_state(TASK_RUNNING);910
/*
Rest
of
the
code
...
*/11
spin_unlock(
可以看到,這段代碼在測試條件之前就將當(dāng)前執(zhí)行進(jìn)程狀態(tài)轉(zhuǎn)設(shè)置成
TASK_INTERRUPTIBLE
了,并且在鏈表不為空的情況下又將自己置為
TASK_RUNNING
狀態(tài)。
這樣一來如果B進(jìn)程在A進(jìn)程進(jìn)程檢查了鏈表為空以后調(diào)用
wake_up_process(),那么A進(jìn)程的狀態(tài)就會自動由原來
TASK_INTERRUPTIBLE
變成
TASK_RUNNING,此后即使進(jìn)程又調(diào)用了
schedule(),由于它現(xiàn)在的狀態(tài)是
TASK_RUNNING,所以仍然不會被從運(yùn)行隊(duì)列中移出,因而不會錯誤的進(jìn)入睡眠,當(dāng)然也就避免了無效喚醒問題。
Linux內(nèi)核的例子
在Linux操作系統(tǒng)中,內(nèi)核的穩(wěn)定性至關(guān)重要,為了避免在Linux操作系統(tǒng)內(nèi)核中出現(xiàn)無效喚醒問題,Linux內(nèi)核在需要進(jìn)程睡眠的時候應(yīng)該使用類似如下的操作:
/*
q
是我們希望睡眠的等待隊(duì)列
*/DECLARE_WAITQUEUE(wait,
current);add_wait_queue(q,
set_current_state(TASK_INTERRUPTIBLE);/*
condition
是等待的條件
*/while
(!condition)
{
schedule();}set_current_state(TASK_RUNNING);remove_wait_queue(q,
上面的操作,使得進(jìn)程通過下面的一系列步驟安全地將自己加入到一個等待隊(duì)列中進(jìn)行睡眠:首先調(diào)用
DECLARE_WAITQUEUE()
創(chuàng)建一個等待隊(duì)列的項(xiàng),然后調(diào)用
add_wait_queue()
把自己加入到等待隊(duì)列中,并且將進(jìn)程的狀態(tài)設(shè)置為
TASK_INTERRUPTIBLE
或者
TASK_INTERRUPTIBLE。
然后循環(huán)檢查條件是否為真:如果是的話就沒有必要睡眠,如果條件不為真,就調(diào)用
schedule()。當(dāng)進(jìn)程檢查的條件滿足后,進(jìn)程又將自己設(shè)置為
TASK_RUNNING
并調(diào)用
remove_wait_queue()
將自己移出等待隊(duì)列。
從上面可以看到,Linux的內(nèi)核代碼維護(hù)者也是在進(jìn)程檢查條件之前就設(shè)置進(jìn)程的狀態(tài)為睡眠狀態(tài),然后才循環(huán)檢查條件。如果在進(jìn)程開始睡眠之前條件就已經(jīng)達(dá)成了,那么循環(huán)會退出并用
set_current_state()
將自己的狀態(tài)設(shè)置為就緒,這樣同樣保證了進(jìn)程不會存在錯誤的進(jìn)入睡眠的傾向,當(dāng)然也就不會導(dǎo)致出現(xiàn)無效喚醒問題。
下面讓我們用Linux內(nèi)核中的實(shí)例來看看其是如何避免無效睡眠的,這段代碼出自Linux2.6的內(nèi)核(/kernel/sched.c):
/*
Wait
for
kthread_stop
*/set_current_state(TASK_INTERRUPTIBLE);while
(!kthread_should_stop())
{
schedule();
set_current_state(TASK_INTERRUPTIBLE);}__set_current_state(TASK_RUNNING);return
0;
上面的這些代碼屬于遷移服務(wù)線程
migration_thread,這個線程不斷地檢查
kthread_should_stop(),直到
kthread_should_stop()
返回1它才可以退出循環(huán),也就是說只要
kthread_should_stop()
返回0該進(jìn)程就會一直睡眠。
從代碼中我們可以看出,檢查
kthread_should_stop()
確實(shí)是在進(jìn)程的狀態(tài)被置為
TASK_INTERRUPTIBLE
后才開始執(zhí)行的。因此,如果在條件檢查之后但是在
schedule()
之前有其他進(jìn)程試圖喚醒它,那么該進(jìn)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 沈陽八上數(shù)學(xué)期中試卷及答案
- 商都二中考試卷及答案
- 肇慶市實(shí)驗(yàn)中學(xué)高中歷史二:第四單元中國社會主義發(fā)展道路的探索測驗(yàn)評講教案
- 2025挖掘機(jī)設(shè)備租賃合同樣本
- 電動機(jī)制造中的綠色制造與環(huán)保標(biāo)準(zhǔn)考核試卷
- 箱包品牌社區(qū)運(yùn)營策略考核試卷
- 硫化鋅納米復(fù)合材料制造技術(shù)考核試卷
- 2025金融科技合作合同范本
- sop考試試題及答案
- 糕點(diǎn)店產(chǎn)品質(zhì)量監(jiān)控與改進(jìn)考核試卷
- JJG 2054-2015振動計(jì)量器具
- GB/T 8303-2013茶磨碎試樣的制備及其干物質(zhì)含量測定
- GB/T 12939-2015工業(yè)車輛輪輞規(guī)格系列
- 博物館藏品庫房管理課件
- 2022年2月興業(yè)銀行審計(jì)部招聘人員模擬試題3套(含答案解析)
- 工貿(mào)行業(yè)重點(diǎn)可燃性粉塵目錄(2015版)
- 焊接質(zhì)量檢查表
- 形式發(fā)票模板
- 高一語文《赤壁賦》 完整版課件PPT
- DB32∕T 2880-2016 光纖傳感式橋隧結(jié)構(gòu)健康監(jiān)測系統(tǒng)設(shè)計(jì)、施工及維護(hù)規(guī)范
- 北師大版小學(xué)數(shù)學(xué)二年級下冊第三單元《練習(xí)二》教學(xué)設(shè)計(jì)建議及課本習(xí)題解析
評論
0/150
提交評論