1.线程间同步
1.1 条件变量
在pthread_cond_wait之前必须要pthread_mutex_lock来保护条件变量不被其他线程修改,同理,pthread_cond_signal下也需要这样的操作,其主要功能如下:
wait流程
- 释放用户锁,以让其他用户可以对条件变量操作;
- 执行wait,等待条件变量满足条件,唤醒线程;
- 加用户锁
#include <pthread.h>
//static initialize
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
//dynamic initialize
pthread_cond_t qready_cond;
pthread_mutex_t qlock_mutex;
pthread_cond_init(&qready_cond, NULL);
pthread_mutex_init(&qlock_mutex,NULL);
//destory
pthread_cond_destroy(pthread_cond_t *cond);
//thread consumer
void function_consumer(){
while(1){
pthread_mutex_lock(&qlock_mutex);
while(xxx == NULL){
pthread_cond_wait(&qready_cond, &qlock_mutex);
}
//critical section
//...
pthread_mutex_lock(&qlock);
}
}
//thread producer
void function_producer(){
while(1){
pthread_mutex_lock(&qlock_mutex);
//critical section
//...
pthread_cond_signal(&qready_cond);
pthread_mutex_lock(&qlock);
}
}
1.并发与竞态
1.1 中断屏蔽
#include <linux/irqflags.h>
local_irq_disable();
local_irq_enable();
local_irq_save(flags);
local_irq_restore(flags);
//常见使用方法
local_irq_disable();
//critical section
local_irq_enable();
1.2 原子操作
#include <asm/atomic.h>
atomic_t v;
//讲原子变量的值初始化为i
v=ATOMIC_INIT(i);
atomic_set(&v, i);
atomic_read(v);//读取原子变量的值
void atomic_add(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
atomic_sub_and_test(i, v);
atomic_dec_and_test(v);
atomic_inc_and_test(v);
atomic_dec_return(v);
atomic_inc_return(v);
int atomic_add_negative(int i, atomic_t *v)
1.3 自旋锁
自旋锁在获取锁之前一直进入忙循环并重复检查锁是否被解锁,可用于不能休眠的流程中,例如中断处理等。
拥有自旋锁时禁止中断;
自旋锁拥有时间尽可能短;
自旋锁下的执行函数不能休眠;
1.3.1 普通自旋锁
#include <linux/spinlock.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
spinlock_t lock=SPIN_LOCK_UNLOCK;
#else
DEFINE_SPINLOCK(lock);
#endif
spin_lock_init(&lock);
void spin_lock(&lock);
void spin_lock_bh(&lock);//获取自旋锁之前禁止软件中断,但硬件中断保持打开
void spin_lock_irq(&lock);//获取自旋锁之前禁止中断
spin_lock_irqsave(&lock, flags);//获取自旋锁之前禁止中断,并将之前的中断状态保存在flags中
void spin_unlock(&lock);
void spin_unlock_bh(&lock);//释放自旋锁,并恢复软件中断
void spin_unlock_irq(&lock);//释放自旋锁,并打开中断
void spin_unlock_irqrestore(&lock, flags);//释放自旋锁,并将之前的中断状态恢复后打开中断
//非阻塞自旋锁获取:
//成功返回非零值,否则返回零
int spin_trylock(&lock);
int spin_trylock_bh(&lock);
int spin_trylock_irq(&lock);
1.3.2 读写自旋锁
#include <linux/rwlock.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
rwlock_t lock=RW_LOCK_UNLOCK;
#else
DEFINE_RWLOCK(lock);
#endif
rwlock_init(&lock);
read_lock(&lock);
read_lock_bh(&lock);//获取自旋锁之前禁止软件中断,但硬件中断保持打开
read_lock_irq(&lock);//获取自旋锁之前禁止中断
read_lock_irqsave(&lock, flags);//获取自旋锁之前禁止中断,并将之前的中断状态保存在flags中
read_unlock(&lock);
read_unlock_bh(&lock);//释放自旋锁,并恢复软件中断
read_unlock_irq(&lock);//释放自旋锁,并打开中断
read_unlock_irqrestore(&lock, flags);//释放自旋锁,并将之前的中断状态恢复后打开中断
write_lock(&lock);
write_lock_bh(&lock);//获取自旋锁之前禁止软件中断,但硬件中断保持打开
write_lock_irq(&lock);//获取自旋锁之前禁止中断
write_lock_irqsave(&lock, flags);//获取自旋锁之前禁止中断,并将之前的中断状态保存在flags中
write_unlock(&lock);
write_unlock_bh(&lock);//释放自旋锁,并恢复软件中断
write_unlock_irq(&lock);//释放自旋锁,并打开中断
write_unlock_irqrestore(&lock, flags);//释放自旋锁,并将之前的中断状态恢复后打开中断
//非阻塞自旋锁获取:
//成功返回非零值,否则返回零
read_trylock(&lock);
read_unlock_irqrestore(&lock, flags);
write_trylock(&lock);
1.4 信号量
信号量在获取过程中会休眠,所以不能用于不能休眠的流程中,例如中断处理等。
1.4.1 普通信号量
#include <asm/semaphore.h>
struct semaphore sem;
//初始化方式一:
void sema_init(&sem,val);
//初始化方式二:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
DECLARE_MUTEX(sem);
#else
DEFINE_SEMAPHORE(sem);
#endif
//初始化方式三:
//在2.6.25后该函数已被废除。请用sema_init(&sem,1);替代
void init_MUTEX(&sem);
void init_MUTEX_LOCKED(&sem);
void down(&sem);//获取信号量
int down_interruptible(&sem);//获取信号量,可被中断
int down_trylock(&sem);//获取信号量,永不休眠
void up(&sem);释放信号量
1.4.2 读写信号量
#include <linux/rwsem.h>
struct rw_semaphore rw_sem;
void init_rwsem(&rw_sem);
void down_read(&rw_sem);//获取信号量
int down_read_trylock(&rw_sem);//获取信号量,永不休眠
void up_read(&rw_sem);释放信号量
void down_write(&rw_sem);//获取信号量
int down_write_trylock(&rw_sem);//获取信号量,永不休眠
void up_write(&rw_sem);释放信号量
void downgrade_write(&rw_sem);
1.5 互斥体
互斥体的使用方法和场景与信号量完全一样。
#include <linux/mutex.h>
struct mutex mux;
mutex_init(mutex);
void mutex_lock(struct mutex *lock);
int __must_check mutex_lock_interruptible(struct mutex *lock);
int mutex_trylock(struct mutex *lock);
void mutex_unlock(struct mutex *lock);
1.6 completion
常用于进程、线程间同步
#include <linux/completion.h>
//不用声明,详细请查看头文件定义
DECLARE_COMPLETION(comp);
//动态创建和初始化
struct completion comp;
void init_completion(&comp);
//等待完成
void wait_for_completion(&comp);
//触发完成
void complete(&comp);
void complete_all(&comp);
1.7 位操作
//定义:include/asm-generic/bitops/atomic.h
#include <asm/bitops.h>
//设置addr地址的第nr位
void set_bit(int nr, volatile unsigned long *addr);
//清除addr地址的第nr位
void clear_bit(int nr, volatile unsigned long *addr);
//切换addr地址的第nr位
void change_bit(int nr, volatile unsigned long *addr);
//设置addr地址的第nr位并返回这个位的先前值
int test_and_set_bit(int nr, volatile unsigned long *addr);
//清除addr地址的第nr位并返回这个位的先前值
int test_and_clear_bit(int nr, volatile unsigned long *addr);
//切换addr地址的第nr位并返回这个位的先前值
int test_and_change_bit(int nr, volatile unsigned long *addr);
1.8 seqlock
#include <linux/seqlock.h>
DEFINE_SEQLOCK(lock);
seqlock_init(lock);
//用于获取seqlock保护的资源的读取访问函数
unsigned read_seqbegin(const seqlock_t *sl);
//用于获取seqlock保护的资源的写入访问函数
void write_seqlock(seqlock_t *sl);
//用于释放seqlock保护的资源的写入访问函数
void write_sequnlock(seqlock_t *sl);
//以上的读写函数还有很多变种,需要的请查看include/linux/seqlock.h文件
1.9 RCU(读取-复制-更新)
#include <linux/rcupdate.h>
void rcu_read_unlock(void);
void rcu_read_lock(void);
void call_rcu(struct rcu_head *head,void (*func)(struct rcu_head *head));