• 沒有找到結果。

第 5 章 ARM LINUX 驱动程序开发入门

5.3 L INUX 驱动程序开发要点

5.3.2 并发控制

在驱动程序中经常会出现这种情况,即多个进程同时访问相同的资源时可能会出现竟态

(race condition),即竞争资源状态,因此我们必须对共享资料进行并发控制。Linux 内核中 解决并发控制最常用的方法是自旋锁(Spinlocks)和信号量(Semaphores)。下面将分别介 绍这两种方法。

5.3.2.1 自旋锁(Spinlocks)

ü spin_lock(spinlock_t *lock);

该函数用于获得自旋锁 lock,如果能够立即获得锁,它就马上返回,否则它将自旋在 那里,直到该自旋锁的保持者释放,这时它获得锁并返回。总之,只有它获得锁才返 回。

ü spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

该函数获得自旋锁的同时把标志寄存器的值保存到变量 flags 中并失效本地中断。

ü spin_lock_irq(spinlock_t *lock);

该函数类似于 spin_lock_irqsave,只是该函数不保存标志寄存器的值。禁止本地中断 并获取指定的锁。

ü spin_lock_bh(spinlock_t *lock);

类似 spin_lock,该函数在获得自旋锁之前要求禁止软件中断,而使硬件中断开启着。

此外,还有两个非阻塞(nonblocking)自旋锁函数,关于非阻塞的概念将在后面的小节 介绍。

ü spin_trylock(spinlock_t *lock);

该函数尽力获得自旋锁 lock,如果能立即获得锁,它获得锁并返回真,否则不能立即 获得锁,立即返回假。它不会自旋等待 lock 被释放。

ü spin_trylock_bh(spinlock_t *lock);

该函数如果获得了自旋锁,它也将失效本地软中断。如果得不到锁,它什么也不做。

因此如果得到了锁,它等同于 spin_lock_bh,如果得不到锁,它等同于 spin_trylock。

如果该宏得到了自旋锁,需要使用 spin_unlock_bh 来释放。

类似的还有以下释放自旋锁的函数:

ü spin_unlock(spinlock_t *lock);

该函数释放自旋锁 lock,它与 spin_trylock 或 spin_lock 配对使用。如果 spin_trylock 返回假,表明没有获得自旋锁,因此不必使用 spin_unlock 释放。

ü spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

该函数释放自旋锁 lock 的同时,也恢复标志寄存器的值为变量 flags 保存的值。它与 spin_lock_irqsave 配对使用。

ü spin_unlock_irq(spinlock_t *lock);

该函数释放自旋锁 lock 的同时,并激活本地中断。它与 spin_lock_irq 配对应用。

ü spin_unlock_bh(spinlock_t *lock);

该函数释放自旋锁 lock 的同时,也使能本地的软中断。它与 spin_lock_bh 配对使用。

关于读自旋锁的 API 函数有:

ü read_lock(rwlock_t *lock);

ü read_lock_irqsave(rwlock_t *lock, unsigned long flags);

ü read_lock_irq(rwlock_t *lock);

ü read_lock_bh(rwlock_t *lock);

ü read_unlock(rwlock_t *lock);

ü read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

ü read_unlock_irq(rwlock_t *lock);

ü read_unlock_bh(rwlock_t *lock);

关于写自旋锁的 API 函数有:

ü write_lock(rwlock_t *lock);

ü write_lock_irqsave(rwlock_t *lock, unsigned long flags);

ü write_lock_irq(rwlock_t *lock);

ü write_lock_bh(rwlock_t *lock);

ü write_trylock(rwlock_t *lock);

ü write_unlock(rwlock_t *lock);

ü write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

ü write_unlock_irq(rwlock_t *lock);

ü write_unlock_bh(rwlock_t *lock);

总之,关于自旋锁的 API 函数还有许多,这里就不再一一介绍了,下面我们可以看一 下自旋锁的通常用法。

……

spinlock_t my_slock = SPIN_LOCK_UNLOCKED;

spin_lock(&my_slock);

上的一个软中断绝不会抢占另一个软中断,因此这种情况下根本不需要禁止下半部。

(mutex,mutual exclusion 的缩写)体。几乎所有的信号量在 Linux 内核中都是用于互斥现 象。

信号量和互斥体的实现相关函数有:

ü sema_init(struct semaphore *sem, int val);

该函数用来初始化一个信号量。其中第一个参数 sem 为指向信号量的指针,val 为赋 给该信号量的初始值。

ü DECLARE_MUTEX(name);

该宏声明一个信号量 name 并初始化它的值为 1,即声明一个互斥锁。

ü DECLARE_MUTEX_LOCKED(name);

该宏声明一个互斥锁 name,但把它的初始值设置为 0,即锁在创建时就处在已锁状 态。因此对于这种锁,一般是先释放后获得。

ü init_MUTEX(struct semaphore *sem);

该 函 数 被 用 在 运 行 时 初 始 化 ( 如 在 动 态 分 配 互 斥 体 的 情 况 下 ), 其 作 用 类 似 DECLARE_MUTEX,即它把信号量 sem 的值设置为 1。

ü init_MUTEX_LOCKED(struct semaphore *sem);

该函数也用于初始化一个互斥锁,但它把信号量 sem 的值设置为 0,即一开始就处在 已锁状态。

ü down(struct semaphore *sem);

该函数用于获得信号量 sem,它会导致睡眠,因此不能在中断上下文(包括 IRQ 上 下文和软中断上下文)使用该函数。该函数将把 sem 的值减 1,如果信号量 sem 的值 非负,就直接返回,否则调用者将被挂起,直到别的任务释放该信号量才能继续运行。

ü down_interruptible(struct semaphore *sem);

该函数功能与 down 类 似,不同之处为 down 不 会被信号(signal)打断,但 down_interruptible 能被信号打断,因此该函数用返回值来区分是正常返回还是被信号 中断,如果返回 0,表示获得信号量正常返回,如果被信号打断,返回-EINTR。

ü down_trylock(struct semaphore *sem);

该函数试着获得信号量 sem,如果能够立刻获得,它就获得该信号量并返回 0,否则,

表示不能获得信号量 sem,返回值为非 0 值。因此它不会导致调用者睡眠,可以在中 断上下文使用。

ü up(struct semaphore *sem);

该函数释放信号量 sem,即把 sem 的值加 1,如果 sem 的值为非正数,表明有任务等 待该信号量,因此唤醒这些等待者。

自旋锁和信号量有很多相似之处又一些本质的不同,其相同之处有:一是它们对互斥来

ü init_rwsem(struct rw_semaphore *sem);

其中 rwsem 是 reader/writer semaphore 的缩写,它是 Linux 内核提供的一种特殊信号 量类型,通过这个函数在运行时初始化 rwsem 的对象。

ü down_read(struct rw_semaphore *sem);

读者调用该函数来得到读写信号量 sem。该函数会导致调用者睡眠,因此只能在进程 上下文使用。

ü down_read_trylock(struct rw_semaphore *sem);

该函数类似于 down_read,只是它不会导致调用者睡眠。它尽力得到读写信号量 sem,

如果能够立即得到,它就得到该读写信号量,并且返回 1,否则表示不能立刻得到该 信号量,返回 0。因此,它也可以在中断上下文使用。

ü up_read(struct rw_semaphore *sem);

读者使用该函数释放读写信号量 sem。它与 down_read 或 down_read_trylock 配对使 用。如果 down_read_trylock 返回 0,不需要调用 up_read 来释放读写信号量,因为根 本就没有获得信号量。

ü down_write(struct rw_semaphore *sem);

写者使用该函数来得到读写信号量 sem,它也会导致调用者睡眠,因此只能在进程上 下文使用。

ü down_write_trylock(struct rw_semaphore *sem);

该函数类似于 down_write,只是它不会导致调用者睡眠。该函数尽力得到读写信号量,

如果能够立刻获得,就获得该读写信号量并且返回 1,否则表示无法立刻获得,返回 0。它可以在中断上下文使用。

ü up_write(struct rw_semaphore *sem);

写者调用该函数释放信号量 sem。它与 down_write 或 down_write_trylock 配对使用。

如果 down_write_trylock 返回 0,不需要调用 up_write,因为返回 0 表示没有获得该 读写信号量。

ü downgrade_write(struct rw_semaphore *sem);

该函数用于把写者降级为读者,这有时是必要的。因为写者是排他性的,因此在写者