设备驱动技术实验五.docx
《设备驱动技术实验五.docx》由会员分享,可在线阅读,更多相关《设备驱动技术实验五.docx(11页珍藏版)》请在冰豆网上搜索。
设备驱动技术实验五
《嵌入式设备驱动技术》
课程实验报告
班级:
学号:
(需要填写)
姓名:
(需要填写)
指导老师:
成绩:
实验五Linux设备驱动中的并发控制
一、目的与任务
目的:
了解Linux内核中的并发控制,及熟悉并发与竞态、中断屏蔽、原子操作、自旋锁、信号量、互斥体机制。
任务:
增加并发控制后的globalmem模块的编程、编译与测试。
二、内容、要求与安排方式
1、实验内容与要求:
1)原子操作;
2)自旋锁;
3)信号量、互斥体机制。
2、实验安排方式:
采用1人1组,上机在Linux系统下进行编程实验。
三、实验设备
1、所用设备:
PC机一台
四、实验过程
1)原子操作;
所谓原子操作,就是该操作绝不会在执行完毕前被任何其他任务或事件打断,也就说,它的最小的执行单位,不可能有比它更小的执行单位,因此这里的原子实际是使用了物理学里的物质微粒的概念。
原子操作需要硬件的支持,因此是架构相关的,其API和原子类型的定义都定义在内核源码树的include/asm/atomic.h文件中,它们都使用汇编语言实现,因为C语言并不能实现这样的操作。
原子操作主要用于实现资源计数,很多引用计数(refcnt)就是通过原子操作实现的。
原子类型定义如下:
volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。
原子操作API包括:
0.该函数对原子类型的变量进行原子读操作,它返回原子类型的变量v的值。
1.该函数设置原子类型的变量v的值为i
atomic_set(atomic_t*v,inti);
2.该函数给原子类型的变量v增加值i。
voidatomic_add(inti,atomic_t*v);
3.该函数从原子类型的变量v中减去i。
atomic_sub(inti,atomic_t*v);
4.该函数从原子类型的变量v中减去i,并判断结果是否为0,如果为0,返回真,否则返回假。
intatomic_sub_and_test(inti,atomic_t*v);
5.该函数对原子类型变量v原子地增加1。
voidatomic_inc(atomic_t*v);
6.该函数对原子类型的变量v原子地减1
voidatomic_dec(atomic_t*v);
7.该函数对原子类型的变量v原子地减1,并判断结果是否为0,如果为0,返回真,否则返回假。
intatomic_dec_and_test(atomic_t*v);
8.该函数对原子类型的变量v原子地增加1,并判断结果是否为0,如果为0,返回真,否则返回假。
intatomic_inc_and_test(atomic_t*v);
9.该函数对原子类型的变量v原子地增加I,并判断结果是否为负数,如果是,返回真,否则返回假。
intatomic_add_negative(inti,atomic_t*v);
10.该函数对原子类型的变量v原子地增加i,并且返回指向v的指针。
intatomic_add_return(inti,atomic_t*v);
11.该函数从原子类型的变量v中减去i,并且返回指向v的指针。
intatomic_sub_return(inti,atomic_t*v);
12.该函数对原子类型的变量v原子地增加1并且返回指向v的指针。
intatomic_inc_return(atomic_t*v);
13.该函数对原子类型的变量v原子地减1并且返回指向v的指针。
intatomic_dec_return(atomic_t*v);
2)自旋锁;
自旋锁原语要求的包含文件是.一个实际的锁有类型spinlock_t.象任何其他数据结构,一个自旋锁必须初始化.这个初始化可以在编译时完成,如下:
spinlock_tmy_lock=SPIN_LOCK_UNLOCKED;
或者在运行时使用:
voidspin_lock_init(spinlock_t*lock);
在进入一个临界区前,你的代码必须获得需要的lock,用:
voidspin_lock(spinlock_t*lock);
注意所有的自旋锁等待是,由于它们的特性,不可中断的.一旦调用spin_lock,将自旋直到锁变为可用.
为释放一个已获得的锁,传递它给:
voidspin_unlock(spinlock_t*lock);
4个函数可以加锁一个自旋锁:
voidspin_lock(spinlock_t*lock);
voidspin_lock_irqsave(spinlock_t*lock,unsignedlongflags);
voidspin_lock_irq(spinlock_t*lock);
voidspin_lock_bh(spinlock_t*lock)
4个方法释放一个自旋锁
voidspin_unlock(spinlock_t*lock);
voidspin_unlock_irqrestore(spinlock_t*lock,unsignedlongflags);
voidspin_unlock_irq(spinlock_t*lock);
voidspin_unlock_bh(spinlock_t*lock);
阻塞的自旋锁操作:
intspin_trylock(spinlock_t*lock);
intspin_trylock_bh(spinlock_t*lock);
2种方式被声明和被初始化:
rwlock_tmy_rwlock=RW_LOCK_UNLOCKED;/*Staticway*/
rwlock_tmy_rwlock;
rwlock_init(&my_rwlock);/*Dynamicway*/
3)信号量、互斥体机制。
为了记录可用资源的数量,需要一个count计数,标记当前可用资源数量。
需要一个count计数和等待进程的链表头即可。
描述信号量的结构体如下。
在linux中,需要一个结构体记录当前的进程信息(task_struct)。
structsemaphore_waiter的list成员是当进程无法获取信号量的时候挂入semaphore的wait_list成员。
task成员就是记录后续被唤醒的进程信息。
一切准备就绪,现在就可以实现信号量的申请函数。
(1).如果信号量标记的资源还有剩余,自然可以成功获取信号量。
只需要递减可用资源计数。
(2).既然无法获取信号量,就需要将当前进程挂入信号量的等待队列链表上。
(3).schedule()主要是触发任务调度的示意函数,主动让出CPU使用权。
在让出之前,需要将当前进程从运行队列上移除。
既然mutex是一种二值信号量,因此就不需要像semaphore那样需要一个count计数。
由于mutex具有“持锁者才能解锁”的特点,所以我们需要一个变量owner记录持锁进程。
释放锁的时候必须是同一个进程才能释放。
当然也需要一个链表头,主要用来便利睡眠等待的进程。
原理和semaphore及其相似,因此在代码上也有体现。
structmutex_waiter的list成员是当进程无法获取互斥量的时候挂入mutex的wait_list链表。
首先实现申请互斥量的函数。
(1).当mutex->owner的值为0的时候,代表没有任何进程持有锁。
因此可以直接申请成功。
然后,记录当前申请锁进程的task_struct。
(2).既然不能获取互斥量,自然就需要睡眠等待,挂入等待链表。
互斥量的释放代码实现也同样和semaphore有很多相似之处。
(1).mutex具有“持锁者才能解锁”的特点就是在这行代码体现。
(2).如果等待链表没有进程,那么自然只需要将mutex->owner置0,代表没有锁是释放状态。
(3).mutex->owner的值改成当前可以持锁进程的task_struct。
(4).从等待进程链表取出第一个进程,并从链表上移除。
然后就是唤醒该进程。
五、程序清单
1)原子操作;
原子操作函数如下:
atomic_read(atomic_t*v);
atomic_set(atomic_t*v,inti);
voidatomic_add(inti,atomic_t*v);
atomic_sub(inti,atomic_t*v);
intatomic_sub_and_test(inti,atomic_t*v);
voidatomic_inc(atomic_t*v);
voidatomic_dec(atomic_t*v);
intatomic_dec_and_test(atomic_t*v);
intatomic_inc_and_test(atomic_t*v);
intatomic_add_negative(inti,atomic_t*v);
intatomic_add_return(inti,atomic_t*v);
intatomic_sub_return(inti,atomic_t*v);
intatomic_inc_return(atomic_t*v);
intatomic_dec_return(atomic_t*v);
2)自旋锁;
自旋锁函数
spinlock_tmy_lock=SPIN_LOCK_UNLOCKED;
voidspin_lock_init(spinlock_t*lock);
voidspin_lock(spinlock_t*lock);
voidspin_unlock(spinlock_t*lock);
voidspin_lock(spinlock_t*lock);
voidspin_lock_irqsave(spinlock_t*lock,unsignedlongflags);
voidspin_lock_irq(spinlock_t*lock);
voidspin_lock_bh(spinlock_t*lock)
voidspin_unlock(spinlock_t*lock);
voidspin_unlock_irqrestore(spinlock_t*lock,unsignedlongflags);
voidspin_unlock_irq(spinlock_t*lock);
voidspin_unlock_bh(spinlock_t*lock);
intspin_trylock(spinlock_t*lock);
intspin_trylock_bh(spinlock_t*lock);
rwlock_tmy_rwlock=RW_LOCK_UNLOCKED;/*Staticway*/
rwlock_tmy_rwlock;
rwlock_init(&my_rwlock);/*Dynamicway*/
3)信号量、互斥体机制。
描述信号量的结构体
structsemaphore{
unsignedintcount;
structlist_headwait_list;
};
结构体记录当前的进程信息
structsemaphore_waiter{
structlist_headlist;
structtask_struct*task;
};
实现信号量的申请函数
voiddown(structsemaphore*sem)
{
structsemaphore_waiterwaiter;
if(sem->count>0){
sem->count--; /*1*/
return;
}
waiter.task=current;/*2*/
list_add_tail(&waiter.list,&sem->wait_list);/*2*/
schedule();/*3*/
}
两个类似的结构体分别描述mutex
structmutex_waiter{
structlist_headlist;
structtask_struct*task;
};
structmutex{
longowner;
structlist_headwait_list;
};
申请互斥量的函数
voidmutex_take(structmutex*mutex)
{
structmutex_waiterwaiter;
if(!
mutex->owner){
mutex->owner=(long)current;/*1*/
return;
}
waiter.task=current;
list_add_tail(&waiter.list,&mutex->wait_list);/*2*/
schedule();/*2*/
}
互斥量的释放代码
intmutex_release(structmutex*mutex)
{
structmutex_waiterwaiter;
if(mutex->owner!
=(long)current)/*1*/
return-1;
if(list_empty(&mutex->wait_list)){
mutex->owner=0;/*2*/
return0;
}
waiter=list_first_entry(&mutex->wait_list,structmutex_waiter,list);
list_del(&waiter->list);
mutex->owner=(long)waiter->task;/*3*/
wake_up_process(waiter->task);/*4*/
return0;
}
六、实验体会
(需要填写)
指导教师评语: