linux內核等待隊列機制:
案例:分析應用程序串口工具操作串口硬件設備的過程。
1.外設的處理速度要遠遠慢于CPU!
2.應用程序在用戶空間沒有權利訪問硬件設備,只有通過系統調用跑到內核空間才有權限訪問硬件設備!
3.一個應用程序讀取串口硬件設備采用兩種方法:
輪詢方式:相當的耗費CPU的資源,讓CPU做大量的無用功!
中斷方式:CPU一旦發現串口設備不可讀(沒數據),CPU干別的事情,一旦串口接收到數據,給CPU產生一個接收中斷信號,讓CPU來獲取串口數據。問:這個應用程序在做什么呢?
當串口設備沒有接收到數據,應用程序一旦發現,利用內核提供的睡眠機制,應用在內核空間進入休眠狀態;一旦串口設備給CPU產生中斷信號,中斷信號的到來也就代表這數據的到來,這時只需喚醒休眠的應用程序,讓應用程序讀取串口數據。
問:如何設備數據沒有準備就緒,如果讓進程在內核空間進行休眠
答:linux內核等待隊列機制
本質目的:就是讓進程在內核空間進行休眠
注意:區別于工作隊列
工作隊列:是中斷底半部的機制,是實現延后執行的一個種手段
等待隊列:是讓進程在內核空間進行休眠的
但是它們針對處理的對象都是進程!
進程的狀態:
運行:TASK_RUNNING
準備就緒:TASK_READY
可中斷休眠:TASK_INTERRUPTIBEL
不可中斷休眠:TASK_UNINTERRUPTIBLE
進程要“休眠":要休眠進程會將CPU資源全部從當前進程中撤下來,將CPU資源給別的任務去使用,比如另外一個進程;
進程之間的切換:又內核調度器來實現,這個調度器就是用來管理進程的!
linux內核等待隊列機制實現過程:
老鷹-》調度器:內核已經實現
雞媽媽-》等待隊列頭
小雞-》休眠的進程
內核描述等待隊列頭涉及的數據類型:
wait_queue_head_t
內核描述休眠的進程,裝載休眠進程的容器的數據類型:
wait_queue_t
等待隊列讓進程休眠的方法,而不是喚醒:
方法1:
步驟:
1.分配等待隊列頭
wait_queue_head_t wq;
2.初始化等待隊列頭
init_waitqueue_head(&wq);
3.如果一個進程要訪問設備,發現設備不可用,進入休眠,此時分配這個進程的休眠的容器
DECLARE_WAITQUEUE(wait, current);
wait:表示保存當前進程的容器
current:它是內核的全局變量,在linux內核中,內核用struct task_struct結構體來描述每一個進程,那么當進程獲取CPU資源是,current就指向當前進程(哪個進程獲取CPU資源,current就指向這個進程對應的task_struct結構體對象)
例如打印出當前進程的pid和name
printk("current process name is %s pid is %d\n",
current->comm, current->pid);
或者:
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
注意:如果有多個休眠的進程,必須為每一個進程分配一個容器,并且current也會分別執行不同的進程!
4.然后將當前進程添加到休眠的隊列中去
add_wait_queue(&wq, &wait);
注意:僅僅是將當前進程添加到這個隊列中去,但進程還處于運行狀態!
5.設置當前進程的休眠狀態(可中斷或者不可中斷)
可中斷的休眠狀態:
current->state = TASK_INTERRUPTIBLE;
不可中斷的休眠狀態:
current->state = TASK_UNINTERRUPTIBLE;
6.讓進程進入真正的休眠狀態
schedule(); //啟動調度器,并且讓CPU資源從當前進程撤下來,給別的進程去使用,至此當前進程運行到這個地方就停止不動!一旦被喚醒,這個函數返回,當前進程接著執行
7.如果進程被設置為可中斷的休眠狀態,進程被喚醒的方法有兩種:
硬件設備可用,產生中斷,由中斷來喚醒;
進程接收到了信號引起的喚醒,所以要判斷喚醒的原因:
判斷是否接收到信號引起的喚醒:
if (signal_pending(current)) {
printk("當前進程是由于接收到了信號引起的喚醒");
printk("給用戶返回-ERESTARTSYS");
} else {
printk("中斷引起的喚醒,說明數據可用");
printk("后續繼續獲取數據");
}
8.不管是硬件中斷引起的喚醒還是信號引起的喚醒,重新設置當前進程的狀態為運行狀態
current->state = TASK_RUNNING;
9.將當前進程從休眠隊列中移除
remove_wait_queue(&wq, &wait);
參考代碼:有一個進程讀取按鍵數據:
wait_queue_head_t wq; //全局變量
驅動入口函數或者open函數:
init_waitqueue_head(&wq);
驅動read函數:
static ssize_t btn_read(...)
{
wait_queue_t wait; //分配一個當前進程的容器
init_waitqueue_entry(&wait, current); //把當前進程添加到這個容器中
add_wait_queue(&wq, &wait);//將當前進程添加到休眠隊列中
current->state = TASK_INTERRUPTIBLE;//設置當前進程的休眠狀態
schedule(); //進入真正的休眠,一旦被喚醒,進程接著執行
//判斷喚醒的原因
if (signal_pending(current)) {
printk("接收到了信號引起的喚醒");
ret = -ERESTARTSYS;
} else {
printk("按鍵有操作,產生中斷引起的喚醒");
}
current->state = TASK_RUNNING;//設置當前進程的狀態為運行
remove_wait_queue(&wq, &wait);//從休眠隊列中移出
上報按鍵數據
return ret;
}
喚醒的方法有兩種:
1.接收到信號引起的喚醒
2.驅動主動喚醒休眠的進程,方法如下:
wake_up(wait_queue_head_t *queue);
喚醒由queue指向的等待隊列數據鏈中的所有睡眠類型的等待進程
wake_up_interruptible(wait_queue_head_t *queue);
喚醒由queue指向的等待隊列數據鏈中的所有睡眠類型為TASK_INTERRUPTIBLE的等待進程
案例1:實現讀進程喚醒寫進程,寫進程喚醒讀進程
實驗步驟:
insmod btn_drv.ko
./led_test r & //讀進程
./led_test w & //寫進程
./led_test r & //讀進程
kill 讀進程或者寫進程
案例2:根據以上案例,在底層驅動的read函數能夠給用戶上報一個按鍵的信息(鍵值和按鍵的狀態),提示可以把底層驅動的write函數作為中斷來使用。
案例3:利用等待隊列,實現按鍵驅動,要求按鍵上報的信息為鍵值和按鍵的狀態!例如:
KEY_UP: 0x50
KEY_DOWN:0x51
按鍵狀態:按下為1,松開為0
分析:
read->fops->cdev->中斷->休眠->等待隊列
指定超時時間的休眠:
把schedule()換成schedule_timeout(5*HZ);
前者的休眠是永久休眠(沒有被驅動主動喚醒或者接收到信號)
后者的休眠是指定了睡眠的時間,例如5秒,如果沒有接收到信號,也沒有接收到驅動主動喚醒,一旦5秒到期,此函數也會返回,返回0,否則返回非0(驅動主動喚醒或者接收到了信號)!
案例:實現按鍵驅動,指定超時,而是永久休眠!
方法2:
1.分配等待隊列頭
wait_queue_head_t wq;
2.初始化等待隊列頭
init_waitqueue_head(&wq);
3.如果進程進入休眠狀態:
wait_event(wq, condition);
condition為真,立即返回,不休眠
condition為假,進程進入不可中斷的休眠狀態,進程被添加 到wq所對應的等待隊列頭中
或者
wait_event_interruptible(wq, condition);
condition為真,立即返回,不休眠
condition為假,進程進入可中斷的休眠狀態,進程被添加 到wq所對應的等待隊列頭中
或者
wait_event_timeout(wq, condition, 5*HZ);
condition為真,立即返回,不休眠
condition為假,進程進入不可中斷的休眠狀態,進程被添加 到wq所對應的等待隊列頭中,并且超時時間到期,進程也被喚醒;
或者
wait_event_interruptible_timeout(wq, condition,5*HZ);
condition為真,立即返回,不休眠
condition為假,進程進入可中斷的休眠狀態,進程被添加 到wq所對應的等待隊列頭中,并且超時時間到期,進程也被喚醒;
總結:以上宏涉及的condition其實就是代表是否是驅動主動喚醒,如果驅動主動喚醒,應該讓condition設置為真,否則還是為假!
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。