新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux驅(qū)動(dòng)總結(jié)3

Linux驅(qū)動(dòng)總結(jié)3

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

關(guān)鍵詞: Linux驅(qū)動(dòng)總

評(píng)論


技術(shù)專區(qū)

關(guān)閉