亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

【世界獨(dú)家】Linux kernel的wait queue機(jī)制

2023-06-15 11:21:06 來(lái)源:Linux與SoC

1. 介紹

當(dāng)編寫(xiě)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í)間后,該過(guò)程將被喚醒,并在我們等待的事件到達(dá)時(shí)繼續(xù)其工作。

等待隊(duì)列是內(nèi)核提供的一種機(jī)制,用于實(shí)現(xiàn)等待。顧名思義,wait queue是等待事件的進(jìn)程列表。換句話說(shuō),當(dāng)某個(gè)條件成立時(shí),等待隊(duì)列用于等待有人叫醒你。它們必須小心使用,以確保沒(méi)有競(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ì)列的名稱(chēng)。

動(dòng)態(tài)方式:

wait_queue_head_twq;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)程?!巴健弊凅w使任何喚醒的進(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

/***************************************************************************//***filedriver.c**detailsSimplelinuxdriver(WaitqueueStaticmethod)**authorxxx*******************************************************************************/#include#include#include#include#include#include#include#include//kmalloc()#include//copy_to/from_user()#include#include//Requiredforthewaitqueues#includeuint32_tread_count=0;staticstructtask_struct*wait_thread;DECLARE_WAIT_QUEUE_HEAD(wait_queue_etx);dev_tdev=0;staticstructclass*dev_class;staticstructcdevetx_cdev;intwait_queue_flag=0;/***FunctionPrototypes*/staticint__initetx_driver_init(void);staticvoid__exitetx_driver_exit(void);/***************Driverfunctions**********************/staticintetx_open(structinode*inode,structfile*file);staticintetx_release(structinode*inode,structfile*file);staticssize_tetx_read(structfile*filp,char__user*buf,size_tlen,loff_t*off);staticssize_tetx_write(structfile*filp,constchar*buf,size_tlen,loff_t*off);/***Fileoperationsturcture*/staticstructfile_operationsfops={.owner=THIS_MODULE,.read=etx_read,.write=etx_write,.open=etx_open,.release=etx_release,};/***Threadfunction*/staticintwait_function(void*unused){while(1){pr_info("WaitingForEvent...");wait_event_interruptible(wait_queue_etx,wait_queue_flag!=0);if(wait_queue_flag==2){pr_info("EventCameFromExitFunction");return0;}pr_info("EventCameFromReadFunction-%d",++read_count);wait_queue_flag=0;}do_exit(0);return0;}/***ThisfunctionwillbecalledwhenweopentheDevicefile*/staticintetx_open(structinode*inode,structfile*file){pr_info("DeviceFileOpened...!!!");return0;}/***ThisfunctionwillbecalledwhenweclosetheDevicefile*/staticintetx_release(structinode*inode,structfile*file){pr_info("DeviceFileClosed...!!!");return0;}/***ThisfunctionwillbecalledwhenwereadtheDevicefile*/staticssize_tetx_read(structfile*filp,char__user*buf,size_tlen,loff_t*off){pr_info("ReadFunction");wait_queue_flag=1;wake_up_interruptible(&wait_queue_etx);return0;}/***ThisfunctionwillbecalledwhenwewritetheDevicefile*/staticssize_tetx_write(structfile*filp,constchar__user*buf,size_tlen,loff_t*off){pr_info("Writefunction");returnlen;}/***ModuleInitfunction*/staticint__initetx_driver_init(void){/*AllocatingMajornumber*/if((alloc_chrdev_region(&dev,0,1,"etx_Dev"))<0){pr_info("Cannotallocatemajornumber");return-1;}pr_info("Major=%dMinor=%d",MAJOR(dev),MINOR(dev));/*Creatingcdevstructure*/cdev_init(&etx_cdev,&fops);etx_cdev.owner=THIS_MODULE;etx_cdev.ops=&fops;/*Addingcharacterdevicetothesystem*/if((cdev_add(&etx_cdev,dev,1))<0){pr_info("Cannotaddthedevicetothesystem");gotor_class;}/*Creatingstructclass*/if(IS_ERR(dev_class=class_create(THIS_MODULE,"etx_class"))){pr_info("Cannotcreatethestructclass");gotor_class;}/*Creatingdevice*/if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){pr_info("CannotcreatetheDevice1");gotor_device;}//Createthekernelthreadwithname"mythread"wait_thread=kthread_create(wait_function,NULL,"WaitThread");if(wait_thread){pr_info("ThreadCreatedsuccessfully");wake_up_process(wait_thread);}elsepr_info("Threadcreationfailed");pr_info("DeviceDriverInsert...Done!!!");return0;r_device:class_destroy(dev_class);r_class:unregister_chrdev_region(dev,1);return-1;}/***Moduleexitfunction*/staticvoid__exitetx_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("DeviceDriverRemove...Done!!!");}module_init(etx_driver_init);module_exit(etx_driver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("xxx");MODULE_DESCRIPTION("Simplelinuxdriver(WaitqueueStaticmethod)");MODULE_VERSION("1.7");

動(dòng)態(tài)創(chuàng)建wait queue

/****************************************************************************//***filedriver.c**detailsSimplelinuxdriver(WaitqueueDynamicmethod)**authorxxx*******************************************************************************/#include#include#include#include#include#include#include#include//kmalloc()#include//copy_to/from_user()#include#include//Requiredforthewaitqueues#includeuint32_tread_count=0;staticstructtask_struct*wait_thread;dev_tdev=0;staticstructclass*dev_class;staticstructcdevetx_cdev;wait_queue_head_twait_queue_etx;intwait_queue_flag=0;/***FunctionPrototypes*/staticint__initetx_driver_init(void);staticvoid__exitetx_driver_exit(void);/***************Driverfunctions**********************/staticintetx_open(structinode*inode,structfile*file);staticintetx_release(structinode*inode,structfile*file);staticssize_tetx_read(structfile*filp,char__user*buf,size_tlen,loff_t*off);staticssize_tetx_write(structfile*filp,constchar*buf,size_tlen,loff_t*off);/***Fileoperationsturcture*/staticstructfile_operationsfops={.owner=THIS_MODULE,.read=etx_read,.write=etx_write,.open=etx_open,.release=etx_release,};/***Threadfunction*/staticintwait_function(void*unused){while(1){pr_info("WaitingForEvent...");wait_event_interruptible(wait_queue_etx,wait_queue_flag!=0);if(wait_queue_flag==2){pr_info("EventCameFromExitFunction");return0;}pr_info("EventCameFromReadFunction-%d",++read_count);wait_queue_flag=0;}return0;}/***ThisfunctionwillbecalledwhenweopentheDevicefile*/staticintetx_open(structinode*inode,structfile*file){pr_info("DeviceFileOpened...!!!");return0;}/***ThisfunctionwillbecalledwhenweclosetheDevicefile*/staticintetx_release(structinode*inode,structfile*file){pr_info("DeviceFileClosed...!!!");return0;}/***ThisfunctionwillbecalledwhenwereadtheDevicefile*/staticssize_tetx_read(structfile*filp,char__user*buf,size_tlen,loff_t*off){pr_info("ReadFunction");wait_queue_flag=1;wake_up_interruptible(&wait_queue_etx);return0;}/***ThisfunctionwillbecalledwhenwewritetheDevicefile*/staticssize_tetx_write(structfile*filp,constchar__user*buf,size_tlen,loff_t*off){pr_info("Writefunction");returnlen;}/***ModuleInitfunction*/staticint__initetx_driver_init(void){/*AllocatingMajornumber*/if((alloc_chrdev_region(&dev,0,1,"etx_Dev"))<0){pr_info("Cannotallocatemajornumber");return-1;}pr_info("Major=%dMinor=%d",MAJOR(dev),MINOR(dev));/*Creatingcdevstructure*/cdev_init(&etx_cdev,&fops);/*Addingcharacterdevicetothesystem*/if((cdev_add(&etx_cdev,dev,1))<0){pr_info("Cannotaddthedevicetothesystem");gotor_class;}/*Creatingstructclass*/if(IS_ERR(dev_class=class_create(THIS_MODULE,"etx_class"))){pr_info("Cannotcreatethestructclass");gotor_class;}/*Creatingdevice*/if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){pr_info("CannotcreatetheDevice1");gotor_device;}//Initializewaitqueueinit_waitqueue_head(&wait_queue_etx);//Createthekernelthreadwithname"mythread"wait_thread=kthread_create(wait_function,NULL,"WaitThread");if(wait_thread){pr_info("ThreadCreatedsuccessfully");wake_up_process(wait_thread);}elsepr_info("Threadcreationfailed");pr_info("DeviceDriverInsert...Done!!!");return0;r_device:class_destroy(dev_class);r_class:unregister_chrdev_region(dev,1);return-1;}/***Moduleexitfunction*/staticvoid__exitetx_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("DeviceDriverRemove...Done!!!");}module_init(etx_driver_init);module_exit(etx_driver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("xxx");MODULE_DESCRIPTION("Simplelinuxdriver(WaitqueueDynamicmethod)");MODULE_VERSION("1.8");

MakeFile

obj-m+=driver.oKDIR=/lib/modules/$(shelluname-r)/buildall:make-C$(KDIR)M=$(shellpwd)modulesclean:make-C$(KDIR)M=$(shellpwd)clean

編譯和測(cè)試

使用Makefile(sudo make)構(gòu)建驅(qū)動(dòng)程序

使用sudo insmod driver.ko加載驅(qū)動(dòng)程序

然后檢查dmesg

Major=246Minor=0ThreadCreatedsuccessfullyDeviceDriverInsert...Done!!!WaitingForEvent...

因此,該線程正在等待該事件?,F(xiàn)在,我們將通過(guò)使用sudo cat/dev/etx_device讀取驅(qū)動(dòng)程序來(lái)發(fā)送事件

現(xiàn)在檢查dmesg

DeviceFileOpened...!!!ReadFunctionEventCameFromReadFunction-1WaitingForEvent...DeviceFileClosed...!!!

我們從讀取功能發(fā)送喚醒,因此它將打印讀取計(jì)數(shù),然后再次休眠。現(xiàn)在通過(guò)sudo rmmod驅(qū)動(dòng)程序從退出功能發(fā)送事件

EventCameFromExitFunctionDeviceDriverRemove...Done!!!

現(xiàn)在條件是2。因此,它將從線程返回并刪除驅(qū)動(dòng)程序。

審核編輯:湯梓紅

標(biāo)簽:

上一篇:從零編寫(xiě)和發(fā)布一個(gè)VSCode擴(kuò)展
下一篇:最后一頁(yè)