linux 并发同步与竞态

1.线程间同步

1.1 条件变量

在pthread_cond_wait之前必须要pthread_mutex_lock来保护条件变量不被其他线程修改,同理,pthread_cond_signal下也需要这样的操作,其主要功能如下:

wait流程

  1. 释放用户锁,以让其他用户可以对条件变量操作;
  2. 执行wait,等待条件变量满足条件,唤醒线程;
  3. 加用户锁
#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));
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值