Linux進程有以下幾種狀態
1、R:運行狀態,并不意味著程序在運行中,它表明進程要么是在運行中要么在運行隊列里。
2、S:休眠狀態,意味著進程在等待事件完成(這里的睡眠有時候也叫做可中斷睡眠)。
3、D:磁盤休眠狀態,有時候也叫做不可中斷睡眠狀態,在這個狀態中進程通常會等待IO結束。
4、T:停止狀態,可以通過發送SIGSTOP信號來停止進程,這個被暫停的進程可以通過發送SIGCONT信號讓進程繼續運行。
5、X:死亡狀態,這個狀態只是一個返回狀態,你不會在列表中看到這個狀態。
6、Z:僵尸狀態,一種瀕臨死亡的狀態。
接下來本文主要對linux進程的睡眠和喚醒進行分析
在Linux中,進程通常是由CPU調度實際執行的。當進程不能繼續執行時,可能是因為它等待某些條件的發生(如I/O操作完成),此時進程會從運行狀態轉換為睡眠狀態(也稱為等待狀態或阻塞狀態)。進程睡眠是通過調用特定的系統調用或者在收到特定的信號時自動進行的。
睡眠的進程會被放入等待隊列,并在等待的事件發生時被喚醒。在內核中,進程睡眠通常是通過將進程的狀態設置為TASK_INTERRUPTIBLE(可中斷睡眠)或TASK_UNINTERRUPTIBLE(不可中斷睡眠),然后調用調度器schedule()來放棄CPU使用權,進程狀態變為就緒態或者運行態。
當進程等待的條件滿足時,通常是由其他進程或者中斷服務例程來喚醒它。喚醒進程的常見方法是修改等待條件,或者發送一個特定的信號給睡眠的進程。
進程的睡眠
進程的睡眠是指進程暫時停止執行,直到滿足某些條件才會繼續執行。睡眠通常發生在以下情況下:
1、I/O 操作:當進程需要等待數據從磁盤、網絡或其他設備讀取時,它會進入睡眠狀態,直到數據準備好。
2、同步操作:如果進程試圖獲取一個已被其他進程占用的資源,它可能會睡眠,直到資源可用。
3、定時器等待:進程可以使用定時器來等待一段時間,然后喚醒并繼續執行。
進程的喚醒
進程的喚醒是指進程從睡眠狀態恢復執行。喚醒通常發生在以下情況下:
1、I/O 完成:當進程等待的I/O操作完成時,它會被喚醒以繼續執行。
2、信號處理:進程可以通過接收信號而被喚醒,然后執行與信號相關的操作。
3、條件滿足:如果進程等待某些條件滿足,一旦這些條件滿足,它將被喚醒。
以下是一個簡單的例子,展示了如何在Linux內核中睡眠和喚醒進程。
c#include <linux/wait.h>
#include <linux/sched.h>
// 定義等待隊列和鎖
DEFINE_WAIT(wait);
struct semaphore lock;
// 初始化信號量
void init_MUTEX(struct semaphore *sem)
{
sema_init(sem, 1);
}
// 獲取信號量
void down(struct semaphore *sem)
{
down_interruptible(sem);
}
// 釋放信號量
void up(struct semaphore *sem)
{
up(sem);
}
// 睡眠的進程
void process_sleep(struct semaphore *sem)
{
add_wait_queue(&sem->wait, &wait);
down(sem);
remove_wait_queue(&sem->wait, &wait);
}
// 喚醒進程
void process_wakeup(struct semaphore *sem)
{
up(sem);
}
// 使用示例
void example_usage()
{
init_MUTEX(&lock);
down(&lock); // 獲取信號量,進程睡眠
// 在其他進程或中斷服務例程中
// 執行必要的操作,然后喚醒睡眠的進程
process_wakeup(&lock);
// 睡眠的進程在被喚醒后繼續執行
up(&lock);
}
在這個例子中,我們定義了一個信號量lock,并初始化為未鎖定狀態。然后,一個進程通過調用process_sleep()進入睡眠狀態,它被放入了信號量lock的等待隊列。另一個進程或中斷服務例程通過調用process_wakeup()來喚醒這個進程,它釋放了信號量lock,喚醒了等待的進程。
注意,實際的睡眠和喚醒操作通常是通過宏或者函數封裝起來,以便于操作系統內核代碼的維護和理解。在用戶空間,應用程序通常使用POSIX提供的同步機制,如信號量、互斥鎖和條件變量等。
在linux進程中會存在有無效喚醒的情況,這種情況是我們需要去避免的
無效喚醒
幾乎在所有的情況下,進程都會在檢查了某些條件之后,發現條件不滿足才進入睡眠。可是有的時候進程卻會在判定條件為真后開始睡眠,如果這樣的話進程就會無限期地休眠下去,這就是所謂的無效喚醒問題。
在操作系統中,當多個進程都企圖對共享數據進行某種處理,而 最后的結果又取決于進程運行的順序時,就會發生競爭條件,這是操作系統中一個典型的問題,無效喚醒恰恰就是由于競爭條件導致的。
設想有兩個進程A 和B,A 進程正在處理一個鏈表,它需要檢查這個鏈表是否為空,如果不空就對鏈表里面的數據進行一些操作,同時B進程也在往這個鏈表添加節點。當這個鏈表是空的時候,由于無數據可操作,這時A進程就進入睡眠,當B進程向鏈表里面添加了節點之后它就喚醒A 進程。
避免無效喚醒
如何避免無效喚醒問題呢?
我們發現無效喚醒主要發生在檢查條件之后和進程狀態被設置為睡眠狀態之前,本來B進程的 wake_up_process() 提供了一次將A進程狀態置為 TASK_RUNNING 的機會,可惜這個時候A進程的狀態仍然是 TASK_RUNNING,所以 wake_up_process() 將A進程狀態從睡眠狀態轉變為運行狀態的努力 沒有起到預期的作用。
要解決這個問題,必須使用一種保障機制使得判斷鏈表為空和設置進程狀態為睡眠狀態成為一個不可分割的步驟才行,也就是必須消除競爭條 件產生的根源,這樣在這之后出現的 wake_up_process() 就可以起到喚醒狀態是睡眠狀態的進程的作用了。