1,访问共享资源的代码区称为临界区(critical sections)
2,解决竟态问题的途径:保证共享资源的互斥访问
3,解决方法:
--中断屏蔽:使中断于进程之间的并发不再发生。
local_irq_disalbe() //屏蔽中断
...
critical sections //临界区
...
local_irq_enable() //开中断
local_irq_save(flags),除了屏蔽中断还能保存目前CPU的中断位信息, local_irq_restore(flags)恢复保存的中断位信息
local_bh_disable()禁止中断的底半部,local_bh_enable()
--原子操作:执行过程中不会被别的代码路径所中断的操作
整型原子操作
//设置原子变量的值
void atomic_set(atomic_t *v, int i); //设置原子变量的值为1
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
//获取原子变量的值
atomic_read(atomic_t *v);
//原子变量加/减
void atomic_add(atomic_t *v);
void atomic_sub(atomic_t *v);
//原子变量自增/自减
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
//操作并测试
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
操作后测试是否为0,为0返回true,否则返回false
//操作并返回
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
操作后返回新值
位原子操作
//设置位,设置addr地址的第nr位为1
void set_bit(nr, void *addr);
//清除位,设置addr地址的第nr位为0
void clear_bit(nr, void *addr);
//改变位, 反位addr地址的第nr的值
void change_bit(nr, void *addr);
//测试位
int test_bit(nr, void *addr);
//测试并操作位
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
--自旋锁(spin lock)
//定义自旋锁
spinlock_t lock;
//初始化自旋锁
spin_lock_init(&lock);
//获得自旋锁
spin_lock(lock); //自旋,直到该自旋锁的持有者释放该自旋锁
spin_trylock(lock); //尝试获得自旋锁lock,获取返回真,没有获得返回假
//释放自旋锁
spin_unlock(lock);
spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock + local_irq_enable()
spin_lock_saveirq() = spin_lock() + local_irq_save()
spin_lock_restoreirq() = spin_unlock() + locak_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_lock() + local_bh_enable()
举例
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(lock);
...
critical section //临界区
...
spin_unlock(lock);
|
读写自旋锁:一种比自旋锁颗粒更小的锁机制,保留了“自旋”的概念,写操作方面,只能最多有一个写进程,读操作方面,同时可以有多个读执行。但读操作与写操作不能同时进行
// 定义和初始化读写自旋锁
/* 静态初始化 */
rwlock_t my_rwlock = RW_LOCK_UNLOCKED;
/* 动态初始化 */
rwlock_t my_rwlock;
rwlock_init(&my_rwlock);
// 读锁定
void read_lock(rwlock_t *my_rwlock);
void read_lock_irqsave(rwlock_t *my_rwlock, unsinged long flags); //read_lock() + local_irq_save(flags)
void read_lock_irq(rwlock_t *my_rwlock); //read_lock() + lock_irq_disable()
void read_lock_bh(rwlock_t *my_rwlock); //read_lock() + lock_bh_disable()
// 读解锁
void read_unlock(rwlock_t *my_rwlock);
void read_unlock_irqrestore(rwlock_t *my_rwlock, unsigned long flags); //read_unlock + local_irq_restore(flags)
void read_unlock_irq(rwlock_t *my_rwlock); //read_unlock() + local_irq_enable()
void read_unlock_bh(rwlock_t *my_rwlock); //read_unlock() + locak_bh_enable()
// 写锁定
void write_lock(rwlock_t *my_rwlock);
void write_lock_irqsave(rwlock_t *my_rwlock, unsigned long flags); //write_lock() + local_irq_save(flags)
void write_lock_irq(rwlock_t *my_rwlock); //write_lock() + local_irq_disable()
void write_lock_bh(rwlock_t *my_rwlock); //write_lock() + local_bh_disable()
// 写解锁
void write_unlock(rwlock_t *my_rwlock);
void write_unlock_irqrestore(rwlock_t *my_rwlock, unsigned long flags); //write_unlock() + local_irq_restore(flags)
void write_unlock_irq(rwlock_t *my_rwlock); //wirte_unlock() + local_irq_enable()
void write_unlock_bh(rwlock_t *my_rwlock); //write_unlock() + local_bh_enable()
顺序锁(seqlock):对读写锁的一种优化,读写操作能同时进行,但写操作与写操作间仍然是互斥的
1, 在LINUX内核中,写执行单元涉及及如下顺序锁操作
//获得顺序锁
void write_seqlock(seqlock_t *sl);
int write_tryseqlock(seqlock_t *sl);
write_seqlock_irqsave(seqlock_t *sl, flags);
write_seqlock_irq(seqlock_t *sl);
write_seqlock_bh(seqlock_t *sl);
|
其中:
write_seqlock_irqsave() = local_irq_save() + write_seqlock()
write_seqlock_irq() = local_irq_disable() + write_seqlock()
write_seqlock_bh() = local_irq_bh() + write_seqlock()
|
//释放顺序锁
void write_sequnlock(seqlock_t *sl);
write_sequnlock_irqrestore(seqlock_t *sl, flags);
write_sequnlock_irq(seqlock_t *sl);
write_sequnlock_bh(seqlock_t *sl);
|
其中:
write_sequnlock_irqrestore() = local_irq_irqrestore() + write_sequnlock()
write_sequnlock_irq() = local_irq_enable() + write_sequnlock()
write_sequnlock_bh() = local_bh_enable() + write_unlock()
|
2,读执行单元涉及如下顺序锁操作
//读开始
read_seqbegin(const seqlock_t *sl);
read_seqbegin_irqsave(lock, flags);
其中:
read_seqbegin_irqsave() = local_irq_save() + read_seqbegin()
|
//重读
int read_seqretry(cost seqlock_t *sl, unsigned iv);
read_seqretry_irgrestore(lock, iv, flags)
其中:
read_seqretry_irqrestore() = read_seqretry() + local_irq_restore()
|
读执行单元使用顺序锁的模式如下:
do{
seqnum = read_seqbegin(&seqlock_a);
//读操作模块
...
}while(read_seqretry(&seqlock_a, seqnum));
|
--信号量
信号量的四种操作:
//定义信号量
//初始化信号量
/* 初始化信号量,并设置信号量的值为val */
void sema_init(struct semaphore *sem, int val);
|
/* 初始化一个用于
互斥的信号量,且信号量的值为1 */
void init_MUTEX(struct semaphore *sem);
|
/* 初始化一个信号量,且值为0 */
void init_MUTEX_LOCKED(struct semaphore *sem);
|
两个宏定义并初始化信号量
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
前者定一个名叫name的信号量且初始值为1,后者为0
|
//
获得信号量
/* 获取信号量,
它会导致睡眠,因此不能在中断上下文中使用 */
void
dowm(struct semaphore *sem)
|
/* 不同与down(),进入睡眠状态的进程能被信号打断,信号也会导致该函数的返回,只是函数返回非0值 */
int
down_interruptible(struct semaphore *sem);
|
/* 尝试获得信号量sem,如果能立即获得返回0,否则,返回非0。它不会导致调用者睡眠,可用于上下文 */
int down_trylock(struct semaphore *sem);
|
使用down_interruptible需要检查返回值
if (down_interruptible(&sem))
{
return -ERESTARTSYS;
}
|
//
释放信号量
/* 释放信号量,唤醒等待着 */
void up(struct semaphore *sem);
|
DECLARE_MUTEX(mount_sem);//定义信号量
down(&mount_sem); //获得信号量
...
critical section //临界区
...
up(&mount_sem); //释放信号量
|