Linux驅(qū)動總結(jié)3
文件操作支持的集合如下:
/*添加該模塊的基本文件操作支持*/
static const struct file_operations mem_fops =
{
/*結(jié)尾不是分號,注意其中的差別*/
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
/*添加新的操作支持*/
.unlocked_ioctl = mem_ioctl,
};
需要注意不是ioctl,而是unlocked_ioctl。
二、設(shè)備的堵塞讀寫方式實現(xiàn),通常采用等待隊列。
設(shè)備的堵塞讀寫方式,默認情況下的讀寫操作都是堵塞型的,具體的就是如果需要讀數(shù)據(jù),當設(shè)備中沒有數(shù)據(jù)可讀的時候應(yīng)該等待設(shè)備中有設(shè)備再讀,當往設(shè)備中寫數(shù)據(jù)時,如果上一次的數(shù)據(jù)還沒有被讀完成,則不應(yīng)該寫入數(shù)據(jù),就會導(dǎo)致進程的堵塞,等待數(shù)據(jù)可讀寫。但是在應(yīng)用程序中也可以采用非堵塞型的方式進行讀寫。只要在打開文件的時候添加一個O_NONBLOCK,這樣在不能讀寫的時候就會直接返回,而不會等待。
因此我們在實際設(shè)計驅(qū)動設(shè)備的同時需要考慮讀寫操作的堵塞方式。堵塞方式的設(shè)計主要是通過等待隊列實現(xiàn),通常是將等待隊列(實質(zhì)就是一個鏈表)的頭作為設(shè)備數(shù)據(jù)結(jié)構(gòu)的一部分。在設(shè)備初始化過程中初始化等待隊列的頭。最后在設(shè)備讀寫操作的實現(xiàn)添加相應(yīng)的等待隊列節(jié)點,并進行相應(yīng)的控制。
等待隊列的操作基本如下:
1、等待隊列的頭定義并初始化的過程如下:
方法一:
struct wait_queue_head_t mywaitqueue;
init_waitqueue_head(&mywaitqueue);
方法二:
DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);
以上的兩種都能實現(xiàn)定義和初始化等待隊列頭。
2、創(chuàng)建、移除一個等待隊列的節(jié)點,并添加、移除相應(yīng)的隊列。
定義一個等待隊列的節(jié)點:DECLARE_WAITQUEUE(wait,tsk)
其中tsk表示一個進程,可以采用current當前的進程。
添加到定義好的等待隊列頭中。
add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:add_wait_queue(&mywaitqueue,&wait);
移除等待節(jié)點
remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:remove_wait_queue(&mywaitqueue,&wait);
3、等待事件
wait_event(queue,condition);當condition為真時,等待隊列頭queue對應(yīng)的隊列被喚醒,否則繼續(xù)堵塞。這種情況下不能被信號打斷。
wait_event_interruptible(queue,condition);當condition為真時,等待隊列頭queue對應(yīng)的隊列被喚醒,否則繼續(xù)堵塞。這種情況下能被信號打斷。
4、喚醒等待隊列
wait_up(wait_queue_head_t *q),喚醒該等待隊列頭對應(yīng)的所有等待。
wait_up_interruptible(wait_queue_head_t *q)喚醒處于TASK_INTERRUPTIBLE的等待進程。
應(yīng)該成對的使用。即wait_event于wait_up,而wait_event_interruptible與wait_up_interruptible。
wait_event和wait_event_interruptible的實現(xiàn)都是采用宏的方式,都是一個重新調(diào)度的過程,如下所示:
#define wait_event_interruptible(wq, condition)
({
int __ret = 0;
if (!(condition))
__wait_event_interruptible(wq, condition, __ret);
__ret;
})
#define __wait_event_interruptible(wq, condition, ret)
do {
/*此處存在一個聲明等待隊列的語句,因此不需要再重新定義一個等待隊列節(jié)點*/
DEFINE_WAIT(__wait);
for (;;) {
/*此處就相當于add_wait_queue()操作,具體參看代碼如下所示*/
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
if (condition)
break;
if (!signal_pending(current)) {
/*此處是調(diào)度,丟失CPU,因此需要wake_up函數(shù)喚醒當前的進程
根據(jù)定義可知,如果條件不滿足,進程就失去CPU,能夠跳出for循環(huán)的出口只有
1、當條件滿足時2、當signal_pending(current)=1時。
1、就是滿足條件,也就是說wake_up函數(shù)只是退出了schedule函數(shù),
而真正退出函數(shù)還需要滿足條件
2、說明進程可以被信號喚醒。也就是信號可能導(dǎo)致沒有滿足條件時就喚醒當前的進程。
這也是后面的代碼采用while判斷的原因.防止被信號喚醒。
*/
schedule();
continue;
}
ret = -ERESTARTSYS;
break;
}
finish_wait(&wq, &__wait);
} while (0)
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
#define DEFINE_WAIT_FUNC(name, function)
wait_queue_t name = {
.private = current,
.func = function,
.task_list = LIST_HEAD_INIT((name).task_list),
}
void 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))
/*添加節(jié)點到等待隊列*/
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
喚醒的操作也是類似的。
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
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);
}
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;
}
}
等待隊列通常用在驅(qū)動程序設(shè)計中的堵塞讀寫操作,并不需要手動的添加節(jié)點到隊列中,直接調(diào)用即可實現(xiàn),具體的實現(xiàn)方法如下:
1、在設(shè)備結(jié)構(gòu)體中添加等待隊列頭,由于讀寫都需要堵塞,所以添加兩個隊列頭,分別用來堵塞寫操作,寫操作。
#include
struct mem_dev
{
char *data;
unsigned long size;
/*添加一個并行機制*/
spinlock_t lock;
/*添加一個等待隊列t頭*/
wait_queue_head_t rdqueue;
wait_queue_head_t wrqueue;
};
2、然后在模塊初始化中初始化隊列頭:
/*初始化函數(shù)*/
static int memdev_init(void)
{
....
for(i = 0; i < MEMDEV_NR_DEVS; i)
{
mem_devp[i].size = MEMDEV_SIZE;
/*對設(shè)備的數(shù)據(jù)空間分配空間*/
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
/*問題,沒有進行錯誤的控制*/
memset(mem_devp[i].data,0,MEMDEV_SIZE);
/*初始化定義的互信息量*/
//初始化定義的自旋鎖ua
spin_lock_init(&(mem_devp[i].lock));
/*初始化兩個等待隊列頭,需要注意必須用括號包含起來,使得優(yōu)先級正確*/
init_waitqueue_head(&(mem_devp[i].rdqueue));
init_waitqueue_head(&(mem_devp[i].wrqueue));
}
評論