linux驱动程序开发3

驱动程序中的并发控制方法:

一个硬件可能会被多个进程并发使用,例如scull_read的时候被另外一个进程调用的scull_write打断,那么读到的数据就不是以前应该读到的数据,这就需要并发控制

并发控制其实多数是使用信号量来完成,包括如下5种方式:1 信号量,2自旋锁,3读写信号量,4读写自旋锁,5completion机制


信号量操作方法:

1 定义及初始化

struct semaphore sem;

可用void sema_init(struct semaphore *sem, int val);直接创建,其中val为信号量初值。

也可以用两个宏来定义和初始化信号量的值为1或0:

DECLARE_MUTEX(name) : 定义信号量name并初始化为1

DECLARE_MUTEX_LOCKED(name) : 定义信号量name并初始化为0

还可以用下面的函数初始化:

void init_MUTEX(struct semaphore *sem); //初始化信号量的值为1

void init_MUTEX_LOCKED(struct semaphore *sem); //初始化信号量的值为0


2 信号量操作

p操作:

void down(struct semaphore *sem); //用来获取信号量,如果信号量值大于或等于0,获取信号量,否则进入睡眠状态,睡眠状态不可唤醒

void down_interruptible(struct semephore *sem); //用来获取信号量,如果信号量大于或等于0,获取信号量,否则进入睡眠状态,等待信号量被释放后,激活该程。

void down_trylock(struct semaphore *sem); //试图获取信号量,如果信号量已被其他进程获取,则立刻返回非零值,调用者不会睡眠

v操作:

void up(struct semaphore *sem); //释放信号量,并唤醒等待该资源进程队列的第一个进程函数

3 使用方法

定义:struct semaphore sem;

初始化:sema_init(&sem, 1);

获取信号量:if(down_interruptible(&sem))

                      return -ERESTARTSYS;

释放信号量:up(&sem);


4注意事项

down为深度睡眠,不能被信号中断;浅度睡眠down_interruptible可以被信号中断,返回值可以用于判断被唤醒的原因是由于其他进程执行了up还是收到了信号。


5 实验

在scull_read中加入:ssleep(5);

写入数据:echo yang>./scull0

并发读出数据:cat ./scull0 & cat ./scull0

查看驱动的输出:tail /var/log/syslog


自旋锁的编程实战:

1 自旋锁的特点及与信号量的区别

(1)自旋锁是一个互斥设备,只有两个值:上锁,解锁

(2)上锁spin_lock进入临界区以后,操作系统不能进行任务调度,只会响应中断,且中断结束后也不进行进程调度,而是回到持有自旋锁的进程

(3)如果获取自旋锁的时候已经被上锁,代码会一直检查这个锁,持续占用cpu,且不会进入休眠,导致占用大量cpu资源,所以只适合临界区代码比较短的场合

(4)持有自旋锁以后的进程不要进入休眠,否则无法释放自旋锁,导致系统死锁。

2 自旋锁的操作方法

(1)定义和初始化

spinlock_t my_lock=SPIN_LOCK_UNLOCKED;

void spin_lock_init(spinlock_t *lock);

(2)自旋锁操作函数

void spin_lock(spinlock_t *lock)  //获取锁

void spin_unlock(spinlock_t *lock) //释放锁

其他函数

void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)//禁止中断,但是保存interrupt中的flag

void spin_lock_irq(spinlock_t *lock)//期间禁止中断

void spin_lock_bh(spinlock_t *lock)//禁止软件中断,使能硬件中断

int spin_trylock(spinlock_t *lock)

int spin_trylock_bh(spinlock_t *lock)


2 使用方法:

(1)定义

spinlock_t lock;

(2)初始化

spin_lock_init(&lock);

(3)获取自旋锁

spin_lock(&lock);

(4)释放自旋锁

spin_unlock(&lock);


3 读写自旋锁

在控制读和写不能并发执行的前提下,使得多个读可以并发执行声明和初始化

rwlock_t my_rwlock=RW_LOCK_UNLOCKED;

rwlock_t my_rwlock;rwlock_init(&my_rwlock);

操作方法:

void read_lock(rwlock_t *lock)

void read_lock_irqsave(rwlock_t *lock, unsigned long flags)

void read_lock_irq(rwlock_t *lock)

void read_lock_bh(rwlock_t *lock)

void read_unlock(rwlock_t *lock)

void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)

void read_unlock_irq(rwlock_t *lock)

void read_unlock_bh(rwlock_t *lock)



Competions机制

编写驱动的时候,经常会做如下操作,在当前线程中初始化并启动另外的线程,并且当前现成必须等待被启动的伙伴线程帮助自己完成某些工作后,自己才能继续执行

内核的信号量api针对可获得的情况进行了专门的优化,当按照上述程序的方式来使用信号量api完成工作,则会导致第一个线程几乎必然阻塞在down调用处,因此会导致性能遭受极大的下降

(1)定义,初始化

struct completion my_completion;

init_completion(&my_completion);

(2)等待完成量

void wait_for_completion(struct completion *c)

(3)唤醒完成量

void complete(struct completion *c)

void complete_all(struct completion *c)

猜你喜欢

转载自blog.csdn.net/oushaojun2/article/details/64123389