全部博文(18)
分类: LINUX
2009-04-09 22:29:16
防止竞争
9.1 原子操作
保证指令的执行过程不被打断。
能使用原子操作的时候,就不使用复杂的加锁机制。
9.1.1 原子整数操作
针对整数的原子操作只能对atomic_t类型数据进行处理;
不使用int的原因:(1) 原子函数只接受atomic_t类型的操作数,确保原子操作只与这种特殊类型数据一起使用;
(2) 保证该类型数据不会被传递给其他任何非原子函数
标准原子整数操作(
Atomic Integer Operation |
Description |
ATOMIC_INIT(int i) |
At declaration, initialize an atomic_t to i |
int atomic_read(atomic_t *v) |
Atomically read the integer value of v |
void atomic_set(atomic_t *v, int i) |
Atomically set v equal to i |
void atomic_add(int i, atomic_t *v) |
Atomically add i to v |
void atomic_sub(int i, atomic_t *v) |
Atomically subtract i from v |
void atomic_inc(atomic_t *v) |
Atomically add one to v |
void atomic_dec(atomic_t *v) |
Atomically subtract one from v |
int atomic_sub_and_test(int i, atomic_t *v) |
Atomically subtract i from v and return true if the result is zero; otherwise false |
int atomic_add_negative(int i, atomic_t *v) |
Atomically add i to v and return true if the result is negative; otherwise false |
int atomic_dec_and_test(atomic_t *v) |
Atomically decrement v by one and return true if zero; false otherwise |
int atomic_inc_and_test(atomic_t *v) |
Atomically increment v by one and return true if the result is zero; false otherwise |
原子操作通常是内联函数,会被定义成一个宏。
原子性与顺序性不同,前者说明指令执行期间不被打断,后者说明指令执行顺序,顺序性用屏障(barrier)来实施。
9.1.2 原子位操作
原子位操作是对普通的内存地址指针进行操作,没有特殊的数据类型。
标准原子位操作列表:
Atomic Bitwise Operation |
Description |
void set_bit(int nr, void *addr) |
Atomically set the nr-th bit starting from addr |
void clear_bit(int nr, void *addr) |
Atomically clear the nr-th bit starting from addr |
void change_bit(int nr, void *addr) |
Atomically flip the value of the nr-th bit starting from addr |
int test_and_set_bit(int nr, void *addr) |
Atomically set the nr-th bit starting from addr and return the previous value |
int test_and_clear_bit(int nr, void *addr) |
Atomically clear the nr-th bit starting from addr and return the previous value |
int test_and_change_bit(int nr, void *addr) |
Atomically flip the nr-th bit starting from addr and return the previous value |
int test_bit(int nr, void *addr) |
Atomically return the value of the nr-th bit starting from addr |
内核提供了一组与上述操作对应的非原子位操作,形式是__test_bit()。
从指定的位置开始搜索第一个被设置(或未被设置)的位:
int find_first_bit(unsigned long *addr, unsigned int size)
int find_first_zero_bit(unsigned long *addr, unsigned int size)
9.2 自旋锁
一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋
=> 浪费处理器时间 => 自旋锁不应该被长时间持有 => 轻量级加锁
自旋锁的实现(
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
spin_lock(&mr_lock);
/* critical region */
spin_unlock(&mr_lock);
自旋锁不可递归
自旋锁可以使用在中断处理程序中,内核提供禁止中断同时请求锁的接口:
|
其他针对自旋锁的操作:
Method |
Description |
spin_lock() |
Acquires given lock |
spin_lock_irq() |
Disables local interrupts and acquires given lock |
spin_lock_irqsave() |
Saves current state of local interrupts, disables local interrupts, and acquires given lock |
spin_unlock() |
Releases given lock |
spin_unlock_irq() |
Releases given lock and enables local interrupts |
spin_unlock_irqrestore() |
Releases given lock and restores local interrupts to given previous state |
spin_lock_init() |
Dynamically initializes given spinlock_t |
spin_trylock() |
Tries to acquire given lock; if unavailable, returns nonzero 试图获得某个自旋锁,若已被争用,返回非0,不会自旋等待锁被释放 |
spin_is_locked() |
Returns nonzero if the given lock is currently acquired, otherwise it returns zero用于检查特定的锁是否被占用,此方法只用作判断 |
9.3 读-写自旋锁
为读和写分别提供不同的锁。
读任务可以并发的持有读者锁,而用于写的锁最多只能被一个写任务持有,此时不能有并发的读操作。
也称为共享/排斥锁、并发/排斥锁。
rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;
读者的代码分支中使用:
read_lock(&mr_rwlock);
/* critical section (read only) ... */
read_unlock(&mr_rwlock);
写者的代码分支中使用:
write_lock(&mr_rwlock);
/* critical section (read and write) ... */
write_unlock(&mr_lock);
不能把一个读锁升级为写锁:
read_lock(&mr_rwlock);
write_lock(&mr_rwlock);
上面的代码会导致死锁。
Method |
Description |
read_lock() |
Acquires given lock for reading |
read_lock_irq() |
Disables local interrupts and acquires given lock for reading |
read_lock_irqsave() |
Saves the current state of local interrupts, disables local interrupts, and acquires the given lock for reading |
read_unlock() |
Releases given lock for reading |
read_unlock_irq() |
Releases given lock and enables local interrupts |
read_unlock_irqrestore() |
Releases given lock and restores local interrupts to the given previous state |
write_lock() |
Acquires given lock for writing |
write_lock_irq() |
Disables local interrupts and acquires the given lock for writing |
write_lock_irqsave() |
Saves current state of local interrupts, disables local interrupts, and acquires the given lock for writing |
write_unlock() |
Releases given lock |
write_unlock_irq() |
Releases given lock and enables local interrupts |
write_unlock_irqrestore() |
Releases given lock and restores local interrupts to given previous state |
write_trylock() |
Tries to acquire given lock for writing; if unavailable, returns nonzero |
rw_lock_init() |
Initializes given rwlock_t |
rw_is_locked() |
Returns nonzero if the given lock is currently acquired, or else it returns zero |
9.4 信号量
是一种睡眠锁;
一个任务试图获得一个已被占用的信号量时,它将被推进等待队列,并睡眠,直到信号量被释放;
期间处理器可以执行其他代码而无需等待;
信号量适用于锁会被长期占有的情况;
只能在进程上下文中才能获取信号量,因为中断上下文不能进行调度;
可以在持有信号量时去睡眠;
互斥信号量:一个时刻仅允许一个锁持有者
计数信号量:一个时刻至多有count个锁持有者
创建和初始化信号量:
static DECLARE_SEMAPHORE_GENERIC(name, count);
创建普通互斥信号量可以使用快捷方式:static DECLARE_MUTEX(name);
若信号量作为一个大数据结构的一部分被动态创建,此时,只有指向该动态创建的信号量的间接指针:
sema_init(sem, count);
初始化一个动态创建的互斥信号量:init_MUTEX(sem);
使用信号量:
down_interruptible() ——获取指定的信号量。若获取失败,则以TASK_INTERRUPTIBLE状态进入睡眠。
down() ——使进程以TASK_UNINTERRUPTIBLE状态进入睡眠,但这样进程在等待信号量的时候就不再响应信号了。
up()——释放指定的信号量
Method |
Description |
sema_init(struct semaphore *, int) |
Initializes the dynamically created semaphore to the given count |
init_MUTEX(struct semaphore *) |
Initializes the dynamically created semaphore with a count of one |
init_MUTEX_LOCKED(struct semaphore *) |
Initializes the dynamically created semaphore with a count of zero (so it is initially locked) |
down_interruptible(struct semaphore *) |
Tries to acquire the given semaphore and enter interruptible sleep if it is contended |
down(struct semaphore *) |
Tries to acquire the given semaphore and enter uninterruptible sleep if it is contended |
down_trylock(struct semaphore *) |
Tries to acquire the given semaphore and immediately return nonzero if it is contended 以堵塞的方式获取指定的信号量,信号量被占用时返回非0,否则返回0 |
up(struct semaphore *) |
Releases the given semaphore and wakes a waiting task, if any |
9.5 读-写信号量:
静态创建:static DECLARE_RWSEM(name);
动态创建:init_rwsem(struct rw_semaphore *sem);
down_read(&mr_rwsem);up_read(&mr_rwsem);
down_write(&mr_rwsem);up_write(&mr_sem);
提供了down_read_trylock()和down_write_trylock() ,若成功获得信号量锁,返回非0,否则返回0(与普通信号量相反)
downgrade_writer()——动态的将获取的写锁转换为读锁
读-写信号量和读-写自旋锁,只有在代码中的读和写可以明白无误的区分开来时才可以使用。
9.6 自旋锁与信号量
二者的比较:
需求 |
建议的加锁方法 |
低开销加锁 |
优先使用自旋锁 |
短期锁定 |
优先使用自旋锁 |
长期加锁 |
优先使用自旋锁 |
中断上下文中加锁 |
使用自旋锁 |
持有锁需要睡眠 |
使用信号量 |
9.7 完成变量
如果一个任务在执行一些工作,另一个任务要发出信号通知它发生了某个特定事件,此时会在完成变量上等待,等第一个任务完成工作后,会使用完成变量去唤醒在等待的任务。
完成变量——completion 定义在
静态创建:DECLARE_COMPLETION(mr_comp);
Method |
Description |
init_completion(struct completion *) |
Initializes the given dynamically created completion variable |
wait_for_completion(struct completion *) |
Waits for the given completion variable to be signaled |
complete(struct completion *) |
Signals any waiting tasks to wake up |
9.8 BKL
大内核锁;
全局自旋锁
Function |
Description |
lock_kernel() |
Acquires the BKL |
unlock_kernel() |
Releases the BKL |
kernel_locked() |
Returns nonzero if the lock is held and zero otherwise (UP always returns nonzero) |
多数时候,BKL是保护代码而不是保护数据的。
Seq锁
用于读写共享数据;
使用序列计数器;
写入数据,序列值增加;
读取数据前后,读取序列号,如果两次读取的序列号相同,则读操作期间没有被写操作打断;
如果读取的值是偶数,则没有写操作发生(锁的初值为0)
定义:seqlock_t mr_seq_lock = SEQLOCK_UNLOCKED;
写锁:
write_seqlock(&mr_seq_lock);
/* write lock is obtained... */
write_sequnlock(&mr_seq_lock);
读锁:
unsigned long seq;
do {
seq = read_seqbegin(&mr_seq_lock);
/* read data here ... */
} while (read_seqretry(&mr_seq_lock, seq));
适用于多个读者和少数写者共享一把锁的情况。
seq锁对写者更有利,没有其他写者,写锁总能成功获得;读锁不会影响写锁
9.9 禁止抢占
Function |
Description |
preempt_disable() |
Disables kernel preemption by incrementing the preemption counter 增加抢占计数,从而禁止内核抢占 |
preempt_enable() |
Decrement the preemption counter and check and service any pending reschedules if the count is now zero 减少抢占计数,当该值为0时间差和指向被挂起的需调度的任务 |
preempt_enable_no_resched() |
Enables kernel preemption but do not check for any pending reschedules 激活内核抢占但不再检查任何被挂起的需调度的任务 |
preempt_count() |
Returns the preemption count 返回抢占计数 |
9.10 顺序和屏障
现代处理器为了优化传送管道 => 打乱分派和提交指令的顺序 => 内存屏障
Barrier |
Description |
rmb() |
Prevents loads from being reordered across the barrier |
read_barrier_depends() |
Prevents data-dependent loads from being reordered across the barrier |
wmb() |
Prevents stores from being reordered across the barrier |
mb() |
Prevents load or stores from being reordered across the barrier |
smp_rmb() |
Provides an rmb() on SMP, and on UP provides a barrier() |
smp_read_barrier_depends() |
Provides a read_barrier_depends() on SMP, and provides a barrier() on UP |
smp_wmb() |
Provides a wmb() on SMP, and provides a barrier() on UP |
smp_mb() |
Provides an mb() on SMP, and provides a barrier() on UP |
barrier() |
Prevents the compiler from optimizing stores or loads across the barrier |
对于不同体系结构,屏障的实际效果差别很大。