新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > Linux驱动总结3

Linux驱动总结3

作者: 时间:2016-12-01 来源:网络 收藏
文件操作支持的集合如下:
/*添加该模块的基本文件操作支持*/
static const struct file_operations mem_fops =
{
/*结尾不是分号,注意其中的差别*/
.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。
二、设备的堵塞读写方式实现,通常采用等待队列。
设备的堵塞读写方式,默认情况下的读写操作都是堵塞型的,具体的就是如果需要读数据,当设备中没有数据可读的时候应该等待设备中有设备再读,当往设备中写数据时,如果上一次的数据还没有被读完成,则不应该写入数据,就会导致进程的堵塞,等待数据可读写。但是在应用程序中也可以采用非堵塞型的方式进行读写。只要在打开文件的时候添加一个O_NONBLOCK,这样在不能读写的时候就会直接返回,而不会等待。
因此我们在实际设计驱动设备的同时需要考虑读写操作的堵塞方式。堵塞方式的设计主要是通过等待队列实现,通常是将等待队列(实质就是一个链表)的头作为设备数据结构的一部分。在设备初始化过程中初始化等待队列的头。最后在设备读写操作的实现添加相应的等待队列节点,并进行相应的控制。
等待队列的操作基本如下:
1、等待队列的头定义并初始化的过程如下:
方法一:
struct wait_queue_head_t mywaitqueue;
init_waitqueue_head(&mywaitqueue);
方法二:
DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);
以上的两种都能实现定义和初始化等待队列头。
2、创建、移除一个等待队列的节点,并添加、移除相应的队列。
定义一个等待队列的节点:DECLARE_WAITQUEUE(wait,tsk)
其中tsk表示一个进程,可以采用current当前的进程。
添加到定义好的等待队列头中。
add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:add_wait_queue(&mywaitqueue,&wait);
移除等待节点
remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:remove_wait_queue(&mywaitqueue,&wait);
3、等待事件
wait_event(queue,condition);当condition为真时,等待队列头queue对应的队列被唤醒,否则继续堵塞。这种情况下不能被信号打断。
wait_event_interruptible(queue,condition);当condition为真时,等待队列头queue对应的队列被唤醒,否则继续堵塞。这种情况下能被信号打断。
4、唤醒等待队列
wait_up(wait_queue_head_t *q),唤醒该等待队列头对应的所有等待。
wait_up_interruptible(wait_queue_head_t *q)唤醒处于TASK_INTERRUPTIBLE的等待进程。
应该成对的使用。即wait_event于wait_up,而wait_event_interruptible与wait_up_interruptible。
wait_event和wait_event_interruptible的实现都是采用宏的方式,都是一个重新调度的过程,如下所示:
#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 {
/*此处存在一个声明等待队列的语句,因此不需要再重新定义一个等待队列节点*/
DEFINE_WAIT(__wait);
for (;;) {
/*此处就相当于add_wait_queue()操作,具体参看代码如下所示*/
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
if (condition)
break;
if (!signal_pending(current)) {
/*此处是调度,丢失CPU,因此需要wake_up函数唤醒当前的进程
根据定义可知,如果条件不满足,进程就失去CPU,能够跳出for循环的出口只有
1、当条件满足时2、当signal_pending(current)=1时。
1、就是满足条件,也就是说wake_up函数只是退出了schedule函数,
而真正退出函数还需要满足条件
2、说明进程可以被信号唤醒。也就是信号可能导致没有满足条件时就唤醒当前的进程。
这也是后面的代码采用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))
/*添加节点到等待队列*/
__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;
}
}
等待队列通常用在驱动程序设计中的堵塞读写操作,并不需要手动的添加节点到队列中,直接调用即可实现,具体的实现方法如下:
1、在设备结构体中添加等待队列头,由于读写都需要堵塞,所以添加两个队列头,分别用来堵塞写操作,写操作。
#include
struct mem_dev
{
char *data;
unsigned long size;
/*添加一个并行机制*/
spinlock_t lock;
/*添加一个等待队列t头*/
wait_queue_head_t rdqueue;
wait_queue_head_t wrqueue;
};
2、然后在模块初始化中初始化队列头:
/*初始化函数*/
static int memdev_init(void)
{
....
for(i = 0; i < MEMDEV_NR_DEVS; i)
{
mem_devp[i].size = MEMDEV_SIZE;
/*对设备的数据空间分配空间*/
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));
/*初始化两个等待队列头,需要注意必须用括号包含起来,使得优先级正确*/
init_waitqueue_head(&(mem_devp[i].rdqueue));
init_waitqueue_head(&(mem_devp[i].wrqueue));
}

关键词: Linux驱动总

评论


技术专区

关闭