1. 介紹
當(dāng)編寫Linux驅(qū)動(dòng)程序、模塊或內(nèi)核程序時(shí),一些進(jìn)程會(huì)等待或休眠一些事件。Linux中有幾種處理睡眠和醒來(lái)的方法,每種方法對(duì)應(yīng)不同的需求,而wait queue便是其中一種。
每當(dāng)進(jìn)程必須等待一個(gè)事件(例如數(shù)據(jù)的到達(dá)或進(jìn)程的終止)時(shí),它都應(yīng)該進(jìn)入睡眠狀態(tài)。睡眠會(huì)導(dǎo)致進(jìn)程暫停執(zhí)行,從而釋放處理器以供其他用途。一段時(shí)間后,該過程將被喚醒,并在我們等待的事件到達(dá)時(shí)繼續(xù)其工作。
等待隊(duì)列是內(nèi)核提供的一種機(jī)制,用于實(shí)現(xiàn)等待。顧名思義,wait queue是等待事件的進(jìn)程列表。換句話說(shuō),當(dāng)某個(gè)條件成立時(shí),等待隊(duì)列用于等待有人叫醒你。它們必須小心使用,以確保沒有競(jìng)爭(zhēng)條件的存在。
實(shí)現(xiàn)wait queue的步驟如下:
初始化等待隊(duì)列
排隊(duì)(將任務(wù)置于睡眠狀態(tài),直到事件發(fā)生)
喚醒排隊(duì)的任務(wù)
以下逐步介紹每個(gè)步驟的實(shí)現(xiàn)方式。
2. 初始化等待隊(duì)列
若使用wait queue功能,需要包含/linux/wait.h頭文件??苫趧?dòng)態(tài)和靜態(tài)兩種方式實(shí)現(xiàn)等待隊(duì)列的初始化。
靜態(tài)方式:
?
DECLARE_WAIT_QUEUE_HEAD(wq);
?
其中,wq是要將任務(wù)置于睡眠狀態(tài)的隊(duì)列的名稱。
動(dòng)態(tài)方式:
?
wait_queue_head_t?wq; init_waitqueue_head?(&wq);
?
除了創(chuàng)建等待隊(duì)列的方式不同之外,其他操作對(duì)于靜態(tài)和動(dòng)態(tài)方法都是相同的。
3. 排隊(duì)
一旦聲明并初始化了等待隊(duì)列,進(jìn)程就可以使用它進(jìn)入睡眠狀態(tài)。有幾個(gè)宏可用于不同的用途。我們將逐一說(shuō)明。
wait_event
wait_event_timeout
wait_event_cmd
wait_event_interruptible
wait_event_interruptible_timeout
wait_event_killable
每當(dāng)我們使用上面的宏時(shí),它會(huì)將該任務(wù)添加到我們創(chuàng)建的等待隊(duì)列中。然后它會(huì)等待事件。
wait_event
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_UNINTERUPTIBLE),直到條件評(píng)估為true。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*/ wait_event(wq,?condition);
?
wait_event_timeout
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_UNINTERUPTIBLE),直到條件評(píng)估為true或超時(shí)。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
如果超時(shí)后條件評(píng)估為false,則返回0;如果超時(shí)后情況評(píng)估為true,則返回1;如果超時(shí)前情況評(píng)估為true,則返回剩余的jiffies(至少1)。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*?timeout?–??超時(shí)時(shí)間,單位jiffies? ?*/ wait_event_timeout(wq,?condition,?timeout);
?
wait_event_cmd
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_UNINTERUPTIBLE),直到條件評(píng)估為true。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*?cmd1–該命令將在睡眠前執(zhí)行 ?*?cmd2–該命令將在睡眠后執(zhí)行 ?*/ wait_event_cmd(wq,?condition,?cmd1,?cmd2);
?
wait_event_interruptible
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_INTERRUPTIBLE),直到條件評(píng)估為真或接收到信號(hào)。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
如果被信號(hào)中斷,函數(shù)將返回-ERESTARTSYS,如果條件評(píng)估為true,則返回0。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*/ wait_event_interruptible(wq,?condition);
?
wait_event_interruptible_timeout
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_INTERRUPTIBLE),直到條件評(píng)估為真或接收到信號(hào)或超時(shí)。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
如果超時(shí)后條件評(píng)估為false,則返回0;如果超時(shí)后情況評(píng)估為true,則返回1;如果超時(shí)前情況評(píng)估為true,則返回剩余的jiffies(至少1);如果被信號(hào)中斷,則返回-ERESTARTSYS。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*?timeout?–??超時(shí)時(shí)間,單位jiffies? ?*/ wait_event_interruptible_timeout(wq,?condition,?timeout);
?
wait_event_killable
進(jìn)程進(jìn)入休眠狀態(tài)(TASK_KILLABLE),直到條件評(píng)估為真或收到信號(hào)。每次喚醒等待隊(duì)列wq時(shí),都會(huì)檢查該條件。
如果被信號(hào)中斷,函數(shù)將返回-ERESTARTSYS,如果條件評(píng)估為true,則返回0。
?
/*?wq?–?等待隊(duì)列 ?*?condition?-?要等待的C表達(dá)式的事件? ?*/ wait_event_killable(wq,?condition);
?
4. 喚醒排隊(duì)的任務(wù)
當(dāng)一些任務(wù)由于等待隊(duì)列而處于睡眠模式時(shí),我們可以使用下面的函數(shù)來(lái)喚醒這些任務(wù)。
wake_up
wake_up_all
wake_up_interruptible
wake_up_sync and wake_up_interruptible_sync
通常,調(diào)用wake_up會(huì)立即觸發(fā)重新調(diào)度,這意味著在wake_up返回之前可能會(huì)運(yùn)行其他進(jìn)程。“同步”變體使任何喚醒的進(jìn)程都可以運(yùn)行,但不會(huì)重新調(diào)度CPU。這用于避免在已知當(dāng)前進(jìn)程進(jìn)入睡眠狀態(tài)時(shí)重新調(diào)度,從而強(qiáng)制重新調(diào)度。注意,被喚醒的進(jìn)程可以立即在不同的處理器上運(yùn)行,因此不應(yīng)期望這些函數(shù)提供互斥
5. 實(shí)踐
我們?cè)趦蓚€(gè)地方發(fā)送了一個(gè)wake_up。一個(gè)來(lái)自讀取功能,另一個(gè)來(lái)自驅(qū)動(dòng)退出。
首先創(chuàng)建了一個(gè)線程(wait_function)。該線程將始終等待該事件。它會(huì)一直睡到接到喚醒事件。當(dāng)它得到wake_up調(diào)用時(shí),它將檢查條件。如果條件為1,則喚醒來(lái)自讀取功能。如果是2,則喚醒來(lái)自退出功能。如果wake_up來(lái)自讀取功能,它將打印讀取計(jì)數(shù),并再次等待。如果它來(lái)自exit函數(shù),那么它將從線程中退出。
靜態(tài)創(chuàng)建wait queue
?
/***************************************************************************//** *??file???????driver.c * *??details????Simple?linux?driver?(Waitqueue?Static?method) * *??author?????xxx *******************************************************************************/ #include?#include? #include? #include? #include? #include? #include? #include? ?????????????????//kmalloc() #include? ??????????????//copy_to/from_user() #include? #include? ?????????????????//?Required?for?the?wait?queues #include? ? ? uint32_t?read_count?=?0; static?struct?task_struct?*wait_thread; ? DECLARE_WAIT_QUEUE_HEAD(wait_queue_etx); ? dev_t?dev?=?0; static?struct?class?*dev_class; static?struct?cdev?etx_cdev; int?wait_queue_flag?=?0; /* **?Function?Prototypes */ static?int??????__init?etx_driver_init(void); static?void?????__exit?etx_driver_exit(void); ? /***************?Driver?functions?**********************/ static?int??????etx_open(struct?inode?*inode,?struct?file?*file); static?int??????etx_release(struct?inode?*inode,?struct?file?*file); static?ssize_t??etx_read(struct?file?*filp,?char?__user?*buf,?size_t?len,loff_t?*?off); static?ssize_t??etx_write(struct?file?*filp,?const?char?*buf,?size_t?len,?loff_t?*?off); /* **?File?operation?sturcture */ static?struct?file_operations?fops?= { ????????.owner??????????=?THIS_MODULE, ????????.read???????????=?etx_read, ????????.write??????????=?etx_write, ????????.open???????????=?etx_open, ????????.release????????=?etx_release, }; /* **?Thread?function */ static?int?wait_function(void?*unused) { ???????? ????????while(1)?{ ????????????????pr_info("Waiting?For?Event... "); ????????????????wait_event_interruptible(wait_queue_etx,?wait_queue_flag?!=?0?); ????????????????if(wait_queue_flag?==?2)?{ ????????????????????????pr_info("Event?Came?From?Exit?Function "); ????????????????????????return?0; ????????????????} ????????????????pr_info("Event?Came?From?Read?Function?-?%d ",?++read_count); ????????????????wait_queue_flag?=?0; ????????} ????????do_exit(0); ????????return?0; } /* **?This?function?will?be?called?when?we?open?the?Device?file */ static?int?etx_open(struct?inode?*inode,?struct?file?*file) { ????????pr_info("Device?File?Opened...!!! "); ????????return?0; } /* **?This?function?will?be?called?when?we?close?the?Device?file */ static?int?etx_release(struct?inode?*inode,?struct?file?*file) { ????????pr_info("Device?File?Closed...!!! "); ????????return?0; } /* **?This?function?will?be?called?when?we?read?the?Device?file */ static?ssize_t?etx_read(struct?file?*filp,?char?__user?*buf,?size_t?len,?loff_t?*off) { ????????pr_info("Read?Function "); ????????wait_queue_flag?=?1; ????????wake_up_interruptible(&wait_queue_etx); ????????return?0; } /* **?This?function?will?be?called?when?we?write?the?Device?file */ static?ssize_t?etx_write(struct?file?*filp,?const?char?__user?*buf,?size_t?len,?loff_t?*off) { ????????pr_info("Write?function "); ????????return?len; } ? /* **?Module?Init?function */ static?int?__init?etx_driver_init(void) { ????????/*Allocating?Major?number*/ ????????if((alloc_chrdev_region(&dev,?0,?1,?"etx_Dev"))?<0){ ????????????????pr_info("Cannot?allocate?major?number "); ????????????????return?-1; ????????} ????????pr_info("Major?=?%d?Minor?=?%d? ",MAJOR(dev),?MINOR(dev)); ? ????????/*Creating?cdev?structure*/ ????????cdev_init(&etx_cdev,&fops); ????????etx_cdev.owner?=?THIS_MODULE; ????????etx_cdev.ops?=?&fops; ? ????????/*Adding?character?device?to?the?system*/ ????????if((cdev_add(&etx_cdev,dev,1))?0){ ????????????pr_info("Cannot?add?the?device?to?the?system "); ????????????goto?r_class; ????????} ? ????????/*Creating?struct?class*/ ????????if(IS_ERR(dev_class?=?class_create(THIS_MODULE,"etx_class"))){ ????????????pr_info("Cannot?create?the?struct?class "); ????????????goto?r_class; ????????} ? ????????/*Creating?device*/ ????????if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){ ????????????pr_info("Cannot?create?the?Device?1 "); ????????????goto?r_device; ????????} ? ????????//Create?the?kernel?thread?with?name?'mythread' ????????wait_thread?=?kthread_create(wait_function,?NULL,?"WaitThread"); ????????if?(wait_thread)?{ ????????????????pr_info("Thread?Created?successfully "); ????????????????wake_up_process(wait_thread); ????????}?else ????????????????pr_info("Thread?creation?failed "); ? ????????pr_info("Device?Driver?Insert...Done!!! "); ????????return?0; ? r_device: ????????class_destroy(dev_class); r_class: ????????unregister_chrdev_region(dev,1); ????????return?-1; } /* **?Module?exit?function */? static?void?__exit?etx_driver_exit(void) { ????????wait_queue_flag?=?2; ????????wake_up_interruptible(&wait_queue_etx); ????????device_destroy(dev_class,dev); ????????class_destroy(dev_class); ????????cdev_del(&etx_cdev); ????????unregister_chrdev_region(dev,?1); ????????pr_info("Device?Driver?Remove...Done!!! "); } ? module_init(etx_driver_init); module_exit(etx_driver_exit); ? MODULE_LICENSE("GPL"); MODULE_AUTHOR("xxx"); MODULE_DESCRIPTION("Simple?linux?driver?(Waitqueue?Static?method)"); MODULE_VERSION("1.7");
?
動(dòng)態(tài)創(chuàng)建wait queue
?
/****************************************************************************//** *??file???????driver.c * *??details????Simple?linux?driver?(Waitqueue?Dynamic?method) * *??author?????xxx *******************************************************************************/ #include?#include? #include? #include? #include? #include? #include? #include? ?????????????????//kmalloc() #include? ??????????????//copy_to/from_user() #include? #include? ?????????????????//?Required?for?the?wait?queues #include? ? ? uint32_t?read_count?=?0; static?struct?task_struct?*wait_thread; ? dev_t?dev?=?0; static?struct?class?*dev_class; static?struct?cdev?etx_cdev; wait_queue_head_t?wait_queue_etx; int?wait_queue_flag?=?0; ? /* **?Function?Prototypes */ static?int??????__init?etx_driver_init(void); static?void?????__exit?etx_driver_exit(void); ? /***************?Driver?functions?**********************/ static?int??????etx_open(struct?inode?*inode,?struct?file?*file); static?int??????etx_release(struct?inode?*inode,?struct?file?*file); static?ssize_t??etx_read(struct?file?*filp,?char?__user?*buf,?size_t?len,loff_t?*?off); static?ssize_t??etx_write(struct?file?*filp,?const?char?*buf,?size_t?len,?loff_t?*?off); /* **?File?operation?sturcture */ static?struct?file_operations?fops?= { ????????.owner??????????=?THIS_MODULE, ????????.read???????????=?etx_read, ????????.write??????????=?etx_write, ????????.open???????????=?etx_open, ????????.release????????=?etx_release, }; ? /* **?Thread?function */ static?int?wait_function(void?*unused) { ???????? ????????while(1)?{ ????????????????pr_info("Waiting?For?Event... "); ????????????????wait_event_interruptible(wait_queue_etx,?wait_queue_flag?!=?0?); ????????????????if(wait_queue_flag?==?2)?{ ????????????????????????pr_info("Event?Came?From?Exit?Function "); ????????????????????????return?0; ????????????????} ????????????????pr_info("Event?Came?From?Read?Function?-?%d ",?++read_count); ????????????????wait_queue_flag?=?0; ????????} ????????return?0; } ? /* **?This?function?will?be?called?when?we?open?the?Device?file */? static?int?etx_open(struct?inode?*inode,?struct?file?*file) { ????????pr_info("Device?File?Opened...!!! "); ????????return?0; } /* **?This?function?will?be?called?when?we?close?the?Device?file */ static?int?etx_release(struct?inode?*inode,?struct?file?*file) { ????????pr_info("Device?File?Closed...!!! "); ????????return?0; } /* **?This?function?will?be?called?when?we?read?the?Device?file */ static?ssize_t?etx_read(struct?file?*filp,?char?__user?*buf,?size_t?len,?loff_t?*off) { ????????pr_info("Read?Function "); ????????wait_queue_flag?=?1; ????????wake_up_interruptible(&wait_queue_etx); ????????return?0; } /* **?This?function?will?be?called?when?we?write?the?Device?file */ static?ssize_t?etx_write(struct?file?*filp,?const?char?__user?*buf,?size_t?len,?loff_t?*off) { ????????pr_info("Write?function "); ????????return?len; } /* **?Module?Init?function */ static?int?__init?etx_driver_init(void) { ????????/*Allocating?Major?number*/ ????????if((alloc_chrdev_region(&dev,?0,?1,?"etx_Dev"))?<0){ ????????????????pr_info("Cannot?allocate?major?number "); ????????????????return?-1; ????????} ????????pr_info("Major?=?%d?Minor?=?%d? ",MAJOR(dev),?MINOR(dev)); ? ????????/*Creating?cdev?structure*/ ????????cdev_init(&etx_cdev,&fops); ? ????????/*Adding?character?device?to?the?system*/ ????????if((cdev_add(&etx_cdev,dev,1))?0){ ????????????pr_info("Cannot?add?the?device?to?the?system "); ????????????goto?r_class; ????????} ? ????????/*Creating?struct?class*/ ????????if(IS_ERR(dev_class?=?class_create(THIS_MODULE,"etx_class"))){ ????????????pr_info("Cannot?create?the?struct?class "); ????????????goto?r_class; ????????} ? ????????/*Creating?device*/ ????????if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){ ????????????pr_info("Cannot?create?the?Device?1 "); ????????????goto?r_device; ????????} ???????? ????????//Initialize?wait?queue ????????init_waitqueue_head(&wait_queue_etx); ? ????????//Create?the?kernel?thread?with?name?'mythread' ????????wait_thread?=?kthread_create(wait_function,?NULL,?"WaitThread"); ????????if?(wait_thread)?{ ????????????????pr_info("Thread?Created?successfully "); ????????????????wake_up_process(wait_thread); ????????}?else ????????????????pr_info("Thread?creation?failed "); ? ????????pr_info("Device?Driver?Insert...Done!!! "); ????????return?0; ? r_device: ????????class_destroy(dev_class); r_class: ????????unregister_chrdev_region(dev,1); ????????return?-1; } /* **?Module?exit?function */ static?void?__exit?etx_driver_exit(void) { ????????wait_queue_flag?=?2; ????????wake_up_interruptible(&wait_queue_etx); ????????device_destroy(dev_class,dev); ????????class_destroy(dev_class); ????????cdev_del(&etx_cdev); ????????unregister_chrdev_region(dev,?1); ????????pr_info("Device?Driver?Remove...Done!!! "); } ? module_init(etx_driver_init); module_exit(etx_driver_exit); ? MODULE_LICENSE("GPL"); MODULE_AUTHOR("xxx"); MODULE_DESCRIPTION("Simple?linux?driver?(Waitqueue?Dynamic?method)"); MODULE_VERSION("1.8");
?
MakeFile
?
obj-m?+=?driver.o KDIR?=?/lib/modules/$(shell?uname?-r)/build all: ????make?-C?$(KDIR)??M=$(shell?pwd)?modules clean: ????make?-C?$(KDIR)??M=$(shell?pwd)?clean
?
編譯和測(cè)試
使用Makefile(sudo make)構(gòu)建驅(qū)動(dòng)程序
使用sudo insmod driver.ko加載驅(qū)動(dòng)程序
然后檢查dmesg
?
Major?=?246?Minor?=?0 Thread?Created?successfully Device?Driver?Insert...Done!!! Waiting?For?Event...
?
因此,該線程正在等待該事件?,F(xiàn)在,我們將通過使用sudo cat/dev/etx_device讀取驅(qū)動(dòng)程序來(lái)發(fā)送事件
現(xiàn)在檢查dmesg
?
Device?File?Opened...!!! Read?Function Event?Came?From?Read?Function?-?1 Waiting?For?Event... Device?File?Closed...!!!
?
我們從讀取功能發(fā)送喚醒,因此它將打印讀取計(jì)數(shù),然后再次休眠。現(xiàn)在通過sudo rmmod驅(qū)動(dòng)程序從退出功能發(fā)送事件
?
Event?Came?From?Exit?Function Device?Driver?Remove...Done!!!
?
現(xiàn)在條件是2。因此,它將從線程返回并刪除驅(qū)動(dòng)程序。
?
審核編輯:湯梓紅
評(píng)論
查看更多