生活随笔
收集整理的這篇文章主要介紹了
Linux设备驱动开发-linux驱动中的阻塞访问方式
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
阻塞與非阻塞是設(shè)備訪問的兩種不同的模式。什么是阻塞操作呢?其是指在執(zhí)行設(shè)備操作的時候,如果不能獲得資源,則掛起進(jìn)程直到滿足可操作的條件后再進(jìn)行操作.而非阻塞操作則是在進(jìn)程不能進(jìn)行設(shè)備操作時,并不掛起到等待隊列,而是放棄或者不斷的查詢,直到能夠進(jìn)行操作。
應(yīng)用程序以阻塞的方式進(jìn)行read操作的時候,會調(diào)用一個system call,將系統(tǒng)的控制權(quán)交給kernel后就進(jìn)入等待狀態(tài),等kernel將這個system執(zhí)行完成后向應(yīng)用程序返回響應(yīng),應(yīng)用程序的得到響應(yīng)后,就推出阻塞狀態(tài),并進(jìn)行后面的工作。
應(yīng)用程序以非阻塞的方式進(jìn)行write操作的時候,通過設(shè)置文件描述符的屬性O(shè)_NONBLOCK使其進(jìn)入非阻塞的訪問狀態(tài),這時進(jìn)程也會調(diào)用相應(yīng)的system call,但是system call會立即從kernel中返回。
從表面上看,阻塞狀態(tài)貌似沒有非阻塞的訪問方式效率高,事實(shí)上卻不是這樣,非阻塞的訪問方式雖然不用等待,會立即返回,可是他不一定就完成了相應(yīng)的工作,比如上面的例子里面,雖然立即返回,但是數(shù)據(jù)可能還沒有真正的寫入文件中,所以說效率的高低并不是表面看上去的那樣。
在linux驅(qū)動中,可以使用等待隊列來實(shí)現(xiàn)阻塞進(jìn)程的喚醒。wait queue以隊列為基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),與進(jìn)程調(diào)度機(jī)制緊密結(jié)合,能夠用于實(shí)現(xiàn)內(nèi)核中的異步事件通知機(jī)制等。下面就先看下一些關(guān)于等待隊列的基本的操作。
定義一個等待隊列頭并初始化:
[cpp]?view plaincopy
wait_queue_head_t?my_queue;?? init_waitqueue_head(&my_queue);??
或者
[cpp]?view plaincopy
DECLARE_WAIT_QUEUE_HEAD(my_queue);??
下面來看下wait_queue_head_t這個結(jié)構(gòu)體,其中t的意思就是typedef的意思,是linux中的一種命名規(guī)則。
[cpp]?view plaincopy
struct?__wait_queue_head?{?? ????????spinlock_t?lock;?? ????????struct?list_head?task_list;?? };?? typedef?struct?__wait_queue_head?wait_queue_head_t;??
首先是定義了一個lock的自旋鎖,后面定義了一個鏈表。其中看下init_waitqueue_head函數(shù),通過wait_queue_head_t結(jié)構(gòu)體成員,就不難想象里面大概的函數(shù)實(shí)現(xiàn):
[cpp]?view plaincopy
extern?void?__init_waitqueue_head(wait_queue_head_t?*q,?struct?lock_class_key?*);?? ?? #define?init_waitqueue_head(q)??????????????????????????\?? ????????do?{????????????????????????????????????????????\?? ????????????????static?struct?lock_class_key?__key;?????\?? ????????????????????????????????????????????????????????\?? ????????????????__init_waitqueue_head((q),?&__key);?????\?? ????????}?while?(0)??
而__init_waitqueue_head()函數(shù)是:
[cpp]?view plaincopy
void?__init_waitqueue_head(wait_queue_head_t?*q,?struct?lock_class_key?*key)?? {?? ????????spin_lock_init(&q->lock);?? ????????lockdep_set_class(&q->lock,?key);?? ????????INIT_LIST_HEAD(&q->task_list);?? }??
其大概也就是初始化自旋鎖以及鏈表等單元。而DECLARE_WAIT_QUEUE_HEAD的函數(shù)原型是:
[cpp]?view plaincopy
#define?DECLARE_WAIT_QUEUE_HEAD(name)?\?? ????????wait_queue_head_t?name?=?__WAIT_QUEUE_HEAD_INITIALIZER(name)??
[cpp]?view plaincopy
#define?__WAIT_QUEUE_HEAD_INITIALIZER(name)?{???????????????????????????\?? ????????.lock???????????=?__SPIN_LOCK_UNLOCKED(name.lock),??????????????\?? ????????.task_list??????=?{?&(name).task_list,?&(name).task_list?}?}??
定義等待隊列用DECLARE_WAITQUEUE函數(shù)來實(shí)現(xiàn)
[cpp]?view plaincopy
DECLARE_WAITQUEUE(name,tsk);??
其定義了一個名為name的等待隊列
[cpp]?view plaincopy
#define?DECLARE_WAITQUEUE(name,?tsk)????????????????????????????????????\?? ????????wait_queue_t?name?=?__WAITQUEUE_INITIALIZER(name,?tsk)??
其中
[cpp]?view plaincopy
typedef?struct?__wait_queue?wait_queue_t;?? ?? struct?__wait_queue?{?? ????????unsigned?int?flags;?? #define?WQ_FLAG_EXCLUSIVE???????0x01?? ????????void?*private;?? ????????wait_queue_func_t?func;?? ????????struct?list_head?task_list;?? };??
[cpp]?view plaincopy
#define?__WAITQUEUE_INITIALIZER(name,?tsk)?{????????????????????????????\?? ????????.private????????=?tsk,??????????????????????????????????????????\?? ????????.func???????????=?default_wake_function,????????????????????????\?? ????????.task_list??????=?{?NULL,?NULL?}?}??
flag:它的值有WQ_FLAG_EXCLUSIVE或者0,他說明等待的進(jìn)程是否是互斥的。當(dāng)為WQ_FLAG_EXCLUSIVE表示互斥;
private:一般用來指向等待進(jìn)程的task_struct實(shí)例;
func:其喚醒等待進(jìn)程;
task_list:用于鏈接等待隊列中的進(jìn)程
下面看下添加和移除等待隊列的API函數(shù):
[cpp]?view plaincopy
void?add_wait_queue(wait_queue_head_t?*q,?wait_queue_t?*wait)?? {?? ????????unsigned?long?flags;?? ?? ????????wait->flags?&=?~WQ_FLAG_EXCLUSIVE;?? ????????spin_lock_irqsave(&q->lock,?flags);?? ????????__add_wait_queue(q,?wait);?? ????????spin_unlock_irqrestore(&q->lock,?flags);?? }?? EXPORT_SYMBOL(add_wait_queue);??
其意思就是將wait等待隊列加入到q的等待隊列頭中。再看其中的__add_wait_queue函數(shù):
[cpp]?view plaincopy
static?inline?void?__add_wait_queue(wait_queue_head_t?*head,?wait_queue_t?*new)?? {?? ????????list_add(&new->task_list,?&head->task_list);?? }??
這樣很容易看出,wait是如何掛到q上面去的。同樣的:
[cpp]?view plaincopy
void?remove_wait_queue(wait_queue_head_t?*q,?wait_queue_t?*wait)?? {?? ????????unsigned?long?flags;?? ?? ????????spin_lock_irqsave(&q->lock,?flags);?? ????????__remove_wait_queue(q,?wait);?? ????????spin_unlock_irqrestore(&q->lock,?flags);?? }?? EXPORT_SYMBOL(remove_wait_queue);??
而__remove_wait_queue函數(shù)
[cpp]?view plaincopy
static?inline?void?__remove_wait_queue(wait_queue_head_t?*head,?? ????????????????????????????????????????????????????????wait_queue_t?*old)?? {????????? ????????list_del(&old->task_list);?? }?????????
這樣看就很簡單了。
下面介紹的是等待事件函數(shù),其就是依據(jù)condition條件是否滿足來選擇是否返回或者阻塞等待。
[cpp]?view plaincopy
wait_event(wq,?condition)?? wait_event_timeout(wq,?condition,?timeout)??? wait_event_interruptible(wq,?condition)??? wait_event_interruptible_timeout(wq,?condition,?timeout)??
下面以此來看上面函數(shù)的實(shí)現(xiàn)過程:
[cpp]?view plaincopy
? ? ? ? ? ? ? ? ? ? ? ?? #define?wait_event(wq,?condition)???????????????????????????????????????\?? do?{????????????????????????????????????????????????????????????????????\?? ????????if?(condition)??????????????????????????????????????????????????\?? ????????????????break;??????????????????????????????????????????????????\?? ????????__wait_event(wq,?condition);????????????????????????????????????\?? }?while?(0)??
其不難看出,當(dāng)condition為真時,函數(shù)立即返回,否則等待條件為真。
[cpp]?view plaincopy
#define?__wait_event(wq,?condition)?????????????????????????????????????\?? do?{????????????????????????????????????????????????????????????????????\?? ????????DEFINE_WAIT(__wait);????????????????????????????????????????????\?? ????????????????????????????????????????????????????????????????????????\?? ????????for?(;;)?{??????????????????????????????????????????????????????\?? ????????????????prepare_to_wait(&wq,?&__wait,?TASK_UNINTERRUPTIBLE);????\?? ????????????????if?(condition)??????????????????????????????????????????\?? ????????????????????????break;??????????????????????????????????????????\?? ????????????????schedule();?????????????????????????????????????????????\?? ????????}???????????????????????????????????????????????????????????????\?? ????????finish_wait(&wq,?&__wait);??????????????????????????????????????\?? }?while?(0)??
這里首先是定義了一個等待隊列項(xiàng)__wait:
[cpp]?view plaincopy
#define?DEFINE_WAIT(name)?DEFINE_WAIT_FUNC(name,?autoremove_wake_function)??
[cpp]?view plaincopy
#define?DEFINE_WAIT_FUNC(name,?function)????????????????????????????????\?? ????????wait_queue_t?name?=?{???????????????????????????????????????????\?? ????????????????.private????????=?current,??????????????????????????????\?? ????????????????.func???????????=?function,?????????????????????????????\?? ????????????????.task_list??????=?LIST_HEAD_INIT((name).task_list),?????\?? ????????}??
其中,.private ?= current表示等待隊列項(xiàng)指向當(dāng)前的進(jìn)程;.func ?= function 其就是的喚醒函數(shù)。
下面就進(jìn)入循環(huán),開始是prepare_to_wait函數(shù),這個函數(shù)的作用是將等待隊列項(xiàng)__wait插入到等待隊列透wq中,然后設(shè)置為TASK_UNINTERRUPTIBLE,即改阻塞狀態(tài)不能被信號打斷,而TASK_INTERRUPTIBLE狀態(tài)可以被信號打斷喚醒。然后再檢查一次condition,當(dāng)condition剛好為真時函數(shù)立即返回,否則調(diào)用schedule()函數(shù)使得進(jìn)程睡眠,執(zhí)行schedule()進(jìn)行了進(jìn)程的切換以后,直到進(jìn)程被喚醒才會調(diào)度該進(jìn)程。for循環(huán)是等進(jìn)程被喚醒后再一次檢查condition條件是否滿足,防止同時喚醒的進(jìn)程已經(jīng)搶先占據(jù)了資源。最后finish_wait將進(jìn)程狀態(tài)屬性改為TASK_RUNNING,并且將進(jìn)程從等待隊列中刪除。下面看下實(shí)現(xiàn)過程:
[cpp]?view plaincopy
prepare_to_wait(wait_queue_head_t?*q,?wait_queue_t?*wait,?int?state)?? {?? ????????unsigned?long?flags;?? ?? ????????wait->flags?&=?~WQ_FLAG_EXCLUSIVE;?? ????????spin_lock_irqsave(&q->lock,?flags);?? ????????if?(list_empty(&wait->task_list))?? ????????????????__add_wait_queue(q,?wait);?? ????????set_current_state(state);?? ????????spin_unlock_irqrestore(&q->lock,?flags);?? }?? EXPORT_SYMBOL(prepare_to_wait);??
[cpp]?view plaincopy
void?finish_wait(wait_queue_head_t?*q,?wait_queue_t?*wait)?? {?? ????????unsigned?long?flags;?? ?? ????????__set_current_state(TASK_RUNNING);?? ????????? ? ? ? ? ? ? ? ? ? ? ? ?? ????????if?(!list_empty_careful(&wait->task_list))?{?? ????????????????spin_lock_irqsave(&q->lock,?flags);?? ????????????????list_del_init(&wait->task_list);?? ????????????????spin_unlock_irqrestore(&q->lock,?flags);?? ????????}?? }?? EXPORT_SYMBOL(finish_wait);??
下面看一下wait_event_timeout()函數(shù)的實(shí)現(xiàn),timeout就是阻塞等待的超時時間,單位是jiffy,當(dāng)timeout達(dá)到以后,不論condition是否滿足函數(shù)都會返回。
[cpp]?view plaincopy
#define?wait_event_timeout(wq,?condition,?timeout)??????????????????????\?? ({??????????????????????????????????????????????????????????????????????\?? ????????long?__ret?=?timeout;???????????????????????????????????????????\?? ????????if?(!(condition))???????????????????????????????????????????????\?? ????????????????__wait_event_timeout(wq,?condition,?__ret);?????????????\?? ????????__ret;??????????????????????????????????????????????????????????\?? })??
[cpp]?view plaincopy
#define?__wait_event_timeout(wq,?condition,?ret)????????????????????????\?? do?{????????????????????????????????????????????????????????????????????\?? ????????DEFINE_WAIT(__wait);????????????????????????????????????????????\?? ????????????????????????????????????????????????????????????????????????\?? ????????for?(;;)?{??????????????????????????????????????????????????????\?? ????????????????prepare_to_wait(&wq,?&__wait,?TASK_UNINTERRUPTIBLE);????\?? ????????????????if?(condition)??????????????????????????????????????????\?? ????????????????????????break;??????????????????????????????????????????\?? ????????????????ret?=?schedule_timeout(ret);????????????????????????????\?? ????????????????if?(!ret)???????????????????????????????????????????????\?? ????????????????????????break;??????????????????????????????????????????\?? ????????}???????????????????????????????????????????????????????????????\?? ????????finish_wait(&wq,?&__wait);??????????????????????????????????????\?? }?while?(0)??
其和前面的區(qū)別就在于多了一個timeout條件,schedule_timeout()函數(shù)設(shè)置了一個ret的時鐘值,他首先調(diào)用schedule()函數(shù),進(jìn)程睡眠,但是每次時鐘中斷的時候它都會檢測時鐘值是否到期,當(dāng)時鐘到期后則返回,正常的返回值是0。
剩余的兩個wait()函數(shù)過程都一樣,這里列出實(shí)現(xiàn)過程:
[cpp]?view plaincopy
#define?wait_event_interruptible(wq,?condition)?????????????????????????\?? ({??????????????????????????????????????????????????????????????????????\?? ????????int?__ret?=?0;??????????????????????????????????????????????????\?? ????????if?(!(condition))???????????????????????????????????????????????\?? ????????????????__wait_event_interruptible(wq,?condition,?__ret);???????\?? ????????__ret;??????????????????????????????????????????????????????????\?? })??
[cpp]?view plaincopy
#define?__wait_event_interruptible(wq,?condition,?ret)??????????????????\?? do?{????????????????????????????????????????????????????????????????????\?? ????????DEFINE_WAIT(__wait);????????????????????????????????????????????\?? ????????????????????????????????????????????????????????????????????????\?? ????????for?(;;)?{??????????????????????????????????????????????????????\?? ????????????????prepare_to_wait(&wq,?&__wait,?TASK_INTERRUPTIBLE);??????\?? ????????????????if?(condition)??????????????????????????????????????????\?? ????????????????????????break;??????????????????????????????????????????\?? ????????????????if?(!signal_pending(current))?{?????????????????????????\?? ????????????????????????schedule();?????????????????????????????????????\?? ????????????????????????continue;???????????????????????????????????????\?? ????????????????}???????????????????????????????????????????????????????\?? ????????????????ret?=?-ERESTARTSYS;?????????????????????????????????????\?? ????????????????break;??????????????????????????????????????????????????\?? ????????}???????????????????????????????????????????????????????????????\?? ????????finish_wait(&wq,?&__wait);??????????????????????????????????????\?? }?while?(0)??
其中wait_event_interruptible()函數(shù)是將進(jìn)程屬性設(shè)置為TASK_INTERRUPTIBLE,可以被信號喚醒,signal_pending(current)函數(shù)是判斷是否是信號喚醒的。是的話直接返回-ERESTARTSYS。
[cpp]?view plaincopy
#define?wait_event_interruptible_timeout(wq,?condition,?timeout)?\?? ({?\?? ????????long?__ret?=?timeout;?\?? ????????if?(!(condition))?\?? ????????????????__wait_event_interruptible_timeout(wq,?condition,?__ret);?\?? ????????__ret;?\?? })??
[cpp]?view plaincopy
#define?__wait_event_interruptible_timeout(wq,?condition,?ret)?\?? do?{?\?? ????????wait_queue_t?__wait;?\?? ????????init_waitqueue_entry(&__wait,?current);?\?? ????????add_wait_queue(&wq,?&__wait);?\?? ????????for?(;;)?{?\?? ????????????????set_current_state(TASK_INTERRUPTIBLE);?\?? ????????????????if?(condition)?\?? ????????????????????????break;?\?? ????????????????if?(!signal_pending(current))?{?\?? ????????????????????????ret?=?schedule_timeout(ret);?\?? ????????????????????????if?(!ret)?\?? ????????????????????????????????break;?\?? ????????????????????????continue;?\?? ????????????????}?\?? ????????????????ret?=?-ERESTARTSYS;?\?? ????????????????break;?\?? ????????}?\?? ????????current->state?=?TASK_RUNNING;?\?? ????????remove_wait_queue(&wq,?&__wait);?\?? }?while?(0)??
[cpp]?view plaincopy
static?inline?void?init_waitqueue_entry(wait_queue_t?*q,?struct?task_struct?*p)?? {?? ????????q->flags?=?0;?? ????????q->private?=?p;?? ????????q->func?=?default_wake_function;?? }??
default_wake_function是內(nèi)核中的一個默認(rèn)的喚醒函數(shù)。
下面來看一下喚醒函數(shù),常用的有:
[cpp]?view plaincopy
#define?wake_up(x)??????????????????????__wake_up(x,?TASK_NORMAL,?1,?NULL)?? #define?wake_up_interruptible(x)????????__wake_up(x,?TASK_INTERRUPTIBLE,?1,?NULL)??
喚醒函數(shù)主要是喚醒屬于指定等待隊列頭的所有等待隊列中等待進(jìn)程。可以看出,其實(shí)質(zhì)都是調(diào)用__wake_up()函數(shù),只是傳遞的參數(shù)不同而已。
[cpp]?view plaincopy
void?__wake_up(wait_queue_head_t?*q,?unsigned?int?mode,?? ????????????????????????int?nr_exclusive,?void?*key)?? {?? ????????unsigned?long?flags;?? ?? ????????spin_lock_irqsave(&q->lock,?flags);?? ????????__wake_up_common(q,?mode,?nr_exclusive,?0,?key);?? ????????spin_unlock_irqrestore(&q->lock,?flags);?? }?? EXPORT_SYMBOL(__wake_up);??
其中
[cpp]?view plaincopy
static?void?__wake_up_common(wait_queue_head_t?*q,?unsigned?int?mode,?? ????????????????????????int?nr_exclusive,?int?wake_flags,?void?*key)?? {?? ????????wait_queue_t?*curr,?*next;?? ?? ????????list_for_each_entry_safe(curr,?next,?&q->task_list,?task_list)?{?? ????????????????unsigned?flags?=?curr->flags;?? ?? ????????????????if?(curr->func(curr,?mode,?wake_flags,?key)?&&?? ????????????????????????????????(flags?&?WQ_FLAG_EXCLUSIVE)?&&?!--nr_exclusive)?? ????????????????????????break;?? ????????}?? }??
list_for_each_entry_safe將遍歷整個等待隊列鏈表,在if語句中,func是默認(rèn)的喚醒函數(shù),是將curr進(jìn)程通過mode方式喚醒,然后再比較是否是互斥形式,如果是的話在判斷需要喚醒的互斥進(jìn)程的數(shù)量(nr_exclusive是需喚醒的互斥進(jìn)程的數(shù)量),通過if語句可以看出,在遍歷的過程中首先先會喚醒非互斥的,然后才會喚醒互斥進(jìn)程(可以通過if語句中&&的順序判斷)。
通過上面的分析,對于等待隊列的阻塞以及喚醒已經(jīng)很清楚了,下面還有一套sleep()函數(shù),其目的是使進(jìn)程在等待隊列上睡眠,如:
[cpp]?view plaincopy
sleep_on(wait_queue_head_t?*q)?? interruptible_sleep_on(wait_queue_head_t?*q)??
sleep_on函數(shù)是將進(jìn)程的狀態(tài)設(shè)置為TASK_UMINTERRUPTIBLE,并且將它附屬到等待隊列頭q上,知道獲得資源,q引導(dǎo)的等待隊列被喚醒。
而interruptible_sleep_on函數(shù)是將進(jìn)程設(shè)置為TASK_INTERRUPTIBLE。
sleep_on與wake_up、interruptible_sleep_on與wake_up_interruptible都是成對出現(xiàn)使用的。
[cpp]?view plaincopy
void?__sched?sleep_on(wait_queue_head_t?*q)?? {?? ????????sleep_on_common(q,?TASK_UNINTERRUPTIBLE,?MAX_SCHEDULE_TIMEOUT);?? }?? EXPORT_SYMBOL(sleep_on);??
[cpp]?view plaincopy
void?__sched?interruptible_sleep_on(wait_queue_head_t?*q)?? {?? ????????sleep_on_common(q,?TASK_INTERRUPTIBLE,?MAX_SCHEDULE_TIMEOUT);?? }?? EXPORT_SYMBOL(interruptible_sleep_on);??
其核心都是sleep_on_common()函數(shù),只是傳遞的參數(shù)不同。
[cpp]?view plaincopy
static?long?__sched?? sleep_on_common(wait_queue_head_t?*q,?int?state,?long?timeout)?? {?? ????????unsigned?long?flags;?? ????????wait_queue_t?wait;?? ?? ????????init_waitqueue_entry(&wait,?current);?? ?? ????????__set_current_state(state);?? ?? ????????spin_lock_irqsave(&q->lock,?flags);?? ????????__add_wait_queue(q,?&wait);?? ????????spin_unlock(&q->lock);?? ????????timeout?=?schedule_timeout(timeout);?? ????????spin_lock_irq(&q->lock);?? ????????__remove_wait_queue(q,?&wait);?? ????????spin_unlock_irqrestore(&q->lock,?flags);?? ?? ????????return?timeout;?? }??
實(shí)現(xiàn)思想與前面所說的都差不多,代碼也比較簡單,這里就不詳細(xì)分析了。
在許多的設(shè)備驅(qū)動中,并不調(diào)用sleep_on()或interruptible_sleep_on(),而是親自進(jìn)行進(jìn)程的狀態(tài)改變和切換,這樣代碼的效率比較高,下面我們根據(jù)前面的globlemem虛擬字符設(shè)備驅(qū)動的例子來進(jìn)行改進(jìn),增加隊列等待機(jī)制,可以對照之前的代碼來看加入阻塞訪問前后的區(qū)別。
首先定義設(shè)備結(jié)構(gòu)體,添加了r_wait和w_wait兩個讀寫的等待隊列頭:
[cpp]?view plaincopy
struct?globalmem_dev{?? ????????struct?cdev?cdev;?? ????unsigned?int?current_len;?? ????????unsigned?char?mem[GLOBALMEM_SIZE];?? ????struct?semaphore?sem;?? ????wait_queue_head_t?r_wait;?? ????wait_queue_head_t?w_wait;?? };??
自然還需要在初始化模塊里面進(jìn)行初始化:
[cpp]?view plaincopy
int?globalmem_init(void)?? {?? ????????int?result;?? ????????dev_t?devno=MKDEV(globalmem_major,0);?? ?? ????????if(globalmem_major)?? ????????????????result=register_chrdev_region(devno,1,"globalmem");?? ????????else{?? ????????????????result=alloc_chrdev_region(&devno,0,1,"globalmem");?? ????????????????globalmem_major=MAJOR(devno);?? ????????}?? ????????if(result<0)?? ????????????????return?result;?? ?? ????????globalmem_devp?=?kmalloc(sizeof(struct?globalmem_dev),GFP_KERNEL);?? ????if(!globalmem_devp){?? ????????result?=?-ENOMEM;?? ????????goto?fail_malloc;?? ????}?? ?? ????memset(&globalmem_devp,0,sizeof(struct?globalmem_dev));?? ?? ????????globalmem_setup_cdev(&globalmem_devp,0);?? ????init_MUTEX(&globalmem_devp->sem);?? ????init_waitqueue_head(&globalfifo_devp->r_wait);??????? ????init_waitqueue_head(&globalfifo_devp->w_wait);?????? ?? ????????return?0;?? ?? fail_malloc:?? ????unregister_chrdev_region(devno,1);?? ????return?result;?? }??
下面在繼續(xù)修改讀寫模塊:
[cpp]?view plaincopy
static?ssize_t?globalmem_read(struct?file?*filp,char?__user?*buf,size_t?count,loff_t?*ppos)?? {?? ????unsigned?long?p?=?*ppos;?? ????int?ret?=?0;?? ????struct?globalmem_dev?*dev?=?filp->private_data;?? ????DECLARE_WAITQUEUE(wait,cuerrent);?????? ?????? ????down(&dev->sem);?? ????add_wait_queue(&dev->r_wait,&wait);?? ?? ????while(dev->current_len==0){?? ????????if(filp->f_flags?&?O_NONBLOCK){?? ????????????ret?=?-EAGAIN;?? ????????????goto?out;????????? ????????}?? ????????__set_current_state(TASK_INTERRUPTIBLE);?? ????????up(&dev->sem);?? ?? ????????schedule();?? ????????if(signal_pending(current)){?? ????????????ret?=?-ERESTARTSYS;?? ????????????goto?out2;???? ????????}????? ?????????? ????????down(&dev->sem);?? ????}?? ?????? ????if(count?>?dev->current_len)?? ????????count?=?dev->current_len;?? ????if(copy_to_user(buf,dev->mem,count)){?? ????????ret?=?-EFAULT;?? ????????goto?out;????? ????}else{?? ????????memcpy(dev->mem,dev->mem+count,dev->current_len-count);?? ????????dev->current_len?-=?count;?? ????????printk(KERN_INFO?"read?%d?bytes(s),current_len:%d\n",count,dev->current_len);?? ?? ????????wake_up_interruptible(&dev->w_wait);?? ?????????? ????????ret?=?count;?????????????? ????}?? ????out:up(&dev->sem);?? ????out2:remove_wait_queue(&dev->r_wait,&wait);?? ????set_current_state(TASK_RUNNING);?? ????return?ret;?? }??
[cpp]?view plaincopy
static?ssize_t?globalmem_write(struct?file?*filp,const?char?__user?*buf,size_t?count,loff_t?*ppos)?? {?? ????unsigned?long?p?=?*ppos;?? ????int?ret?=?0;?? ????struct?globalmem_dev?*dev?=?filp->private_data;?? ????DECLARE_WAITQUEUE(wait,cuerrent);?????? ?????? ????down(&dev->sem);?? ????add_wait_queue(&dev->w_wait,&wait);?? ?? ????while(dev->current_len==GLOBALFIFO_SIZE){?? ????????if(filp->f_flags?&?O_NONBLOCK){?? ????????????ret?=?-EAGAIN;?? ????????????goto?out;????????? ????????}?? ????????__set_current_state(TASK_INTERRUPTIBLE);?? ????????up(&dev->sem);?? ?? ????????schedule();?? ????????if(signal_pending(current)){?? ????????????ret?=?-ERESTARTSYS;?? ????????????goto?out2;???? ????????}????? ?????????? ????????down(&dev->sem);?? ????}?? ?????? ????if(count?>?GLOBALFIFO_SIZE-dev->current_len)?? ????????count?=?GLOBALFIFO-dev->current_len;?? ????if(copy_from_user(dev->mem+dev->current_len,buf,count)){?? ????????ret?=?-EFAULT;?? ????????goto?out;????? ????}else{?? ????????dev->current_len?+=?count;?? ????????printk(KERN_INFO?"written?%d?bytes(s),current_len:%d\n",count,dev->current_len);?? ?? ????????wake_up_interruptible(&dev->r_wait);?? ?????????? ????????ret?=?count;?????????????? ????}?? ????out:up(&dev->sem);?? ????out2:remove_wait_queue(&dev->w_wait,&wait);?? ????set_current_state(TASK_RUNNING);?? ????return?ret;?? }??
其并沒有調(diào)用seelp_on()等函數(shù),選擇自己設(shè)置狀態(tài)以及進(jìn)程的切換等動作。將上述的過程用wait_event_interruptible()函數(shù)替換的話,可能會出現(xiàn)死鎖的狀態(tài),可以自己思考一下這個過程。上面讀緩沖區(qū)的數(shù)據(jù)需要在寫函數(shù)中喚醒r_wait,才可以進(jìn)行讀的操作,而進(jìn)行寫的過程需要在讀函數(shù)中喚醒w_wait才可以寫入。
總結(jié)
以上是生活随笔為你收集整理的Linux设备驱动开发-linux驱动中的阻塞访问方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。