Chinaunix首页 | 论坛 | 博客
  • 博客访问: 22427
  • 博文数量: 18
  • 博客积分: 810
  • 博客等级: 准尉
  • 技术积分: 205
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-10 21:09
文章分类
文章存档

2009年(15)

2008年(3)

我的朋友

分类: 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);

自旋锁不可递归

自旋锁可以使用在中断处理程序中,内核提供禁止中断同时请求锁的接口:

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags;
spin_lock_irqsave(&mr_lock, flags); //保存中断的当前状态,并禁止本地中断,然后再去获取指定的锁

/* critical region ... */
spin_unlock_irqrestore(&mr_lock, flags);//对指定的锁解锁,将中断恢复到加锁前的状态


这种方法即使中断最初是禁止的,也不会错误的激活它们。

若能确定中断在加锁前是激活的,就不需要解锁后恢复中断以前的状态,可以无条件的在解锁是激活中断:

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
spin_lock_irq(&mr_lock);
/* critical section ... */
spin_unlock_irq(&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

对于不同体系结构,屏障的实际效果差别很大。

阅读(333) | 评论(0) | 转发(0) |
0

上一篇:Chap8 内核同步介绍

下一篇:Chap13 块I/O层

给主人留下些什么吧!~~