在linux 驅動程式中,可以使用等待佇列來實現阻塞程序的喚醒。等待佇列的頭部定義如下,是乙個雙向列表。
struct list_head }
#define declare_waitqueue(name, tsk) \
wait_queue_t name = __waitqueue_initializer(name, tsk)
其實就是兩個巨集定義,這個兩個巨集定義擴充套件開來其實就是
wait_queue_t name= } #等待佇列
那麼回到**中的呼叫declare_waitqueue(wait, current)就是定義了乙個等待佇列元素wait。等待佇列的private就等於當前程序指標。
3 add_wait_queue/remove_wait_queue:新增移除等待佇列
4 等待事件
wait_event(queue,condition)
wait_event_interrupt(queue,condition)
wait_event_timeout(queue,condition,timeout)
wait_event_interruptible_timeout(queue,condition,timeout)
等待第1個引數queue作為等待佇列頭部的佇列被喚醒,而且第2個引數必須滿足,否則繼續阻塞,wait_event和wait_event_interrupt的區別在於後者可以被訊號中斷打斷。加上timeout後的巨集意味著阻塞等待的超時時間,在第三個引數timeout到達時,不論condition是否滿足,均返回。
來看下**的實現,首先是wait_event。 先判斷條件,如果條件,則立即退出,否則進入__wait_event
#define wait_event(wq, condition) \
do while (0)
#define __wait_event(wq, condition) \
do while (0)
(1) define_wait(__wait) 中申明乙個當前進展的等待佇列
#define define_wait(name) define_wait_func(name, autoremove_wake_function)
#define define_wait_func(name, function) \
wait_queue_t name = while (0)
5 wake_up(wait_queue_head_t *queue) wake_up會喚醒作為等待佇列頭部的佇列中的所有程序。**按如下,遍歷佇列,然後依次執行喚醒**函式
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) ;
struct globalmem_dev *globalmem_devp;
static
int globalmem_open(struct inode *inode,struct file *filp)
static
int globalmem_release(struct inode *inode,struct file *filp)
static
long globalmem_ioctl(struct file *filp,unsigned int cmd,unsigned long
arg)
return0;
}static ssize_t globalmem_read_queue(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
__set_current_state(task_interruptible);
mutex_unlock(&dev->mutex);
schedule();
if(signal_pending(current))
mutex_lock(&dev->mutex);
}if(size > dev->current_len)
size=dev->current_len;
if(copy_to_user(buf,dev->mem,size))
else
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait,&wait);
set_current_state(task_running);
return
ret;
}static ssize_t globalmem_write_queue(struct file *filp,const
char __user *buf,size_t size, loff_t *ppos)
__set_current_state(task_interruptible);
mutex_unlock(&dev->mutex);
schedule();
if(signal_pending(current))
mutex_lock(&dev->mutex);
} if(size > globalmem_size- dev->current_len)
size=globalmem_size - dev->current_len;
if(copy_from_user(dev->mem+dev->current_len,buf,size))
else
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait,&wait);
set_current_state(task_running);
return
ret;
}static loff_t globalmem_llseek(struct file *filp,loff_t offset,int
orig)
filp->f_pos=(unsigned int
)offset;
ret=filp->f_pos;
break
;
case1:
if((filp->f_pos+offset) >globalmem_size)
if((filp->f_pos+offset) < 0
) filp->f_pos+=offset;
ret=filp->f_pos;
break
; }
return
ret;
}static
const
struct file_operations globalmem_fops=;
static
void globalmem_setup_dev(struct globalmem_dev *dev,int
index)
static
int __init globalmem_init(void
)
if(ret < 0
)
return
ret;
globalmem_devp=kzalloc(sizeof(struct
globalmem_dev),gfp_kernel);
if(!globalmem_devp)
mutex_init(&globalmem_devp->mutex);
globalmem_setup_dev(globalmem_devp,0);
printk(
"globalmem init success\n");
init_waitqueue_head(&globalmem_devp->r_wait);
init_waitqueue_head(&globalmem_devp->w_wait);
return0;
fail_malloc:
unregister_chrdev_region(devno,1);
return
ret;
}module_init(globalmem_init);
static
void __exit globalmem_exit(void
)module_exit(globalmem_exit);
module_author(
"zhf");
module_license(
"gpl
");**主要做了如下幾點改動:
1 在globalmem_dev中增加讀和寫的佇列r_wait以及w_wait
2 在globalmem_init中呼叫init_waitqueue_head初始化寫和讀佇列
3 在globalmem_read_queue中將當前程序加入讀的等待佇列,只有當讀佇列完成copy_to_user的操作後,才喚醒寫佇列的程序
4 在globalmem_write_queue中將當前程序加入寫的等待佇列,只有當寫佇列完成copy_from_user的操作後,才喚醒讀佇列的程序
Linux驅動開發學習(一)
1.軟體驅動 驅動硬體,使硬體處於某種工作模式,提供控制硬體方法 2.驅動的地位 驅動是連線核心與裝置的橋梁 1.字元裝置 字元裝置驅動 字元裝置檔案 2.網路裝置 網路裝置驅動 3.塊裝置 塊裝置驅動 塊裝置檔案 1.驅動編寫 2.驅動編譯 3.驅動使用 三要素 1.入口 載入 module in...
Linux驅動學習(二)
注 基於linux 2.6.38 還是 arch arm mach s3c64xx mach mini6410.c這個檔案,前面有篇文章已經說了裡面的mini6410 machine init 函式是什麼時候被呼叫的,因此在這裡不再重複,直接看這個函式裡面的內容 1 static void init...
Linux驅動開發學習筆記
一 linux裝置驅動基礎 基於linux2.6核心 參考 linux device drivers 3rd edition linux kernel有乙個很好的特性,可以支援在執行是進行擴充套件。這意味著系統啟動執行是,我們仍然可以向kernel新增功能。這種執行時可以被新增到kernel的 稱為...