4.2并发和竞争

什么情况会出现并发和竞争问题

在以下情况会存在并发:

  1. 进程与进程之间,一个进程会被另一个进程打断,或进程时间片结束自动放弃CPU, 或多个CPU核心同时运行多个进程
  2. 进程和软中断之间,本核心的进程可能会被本核心的软中断打断
  3. 进程和硬中断之间,本核心的进程可能会被本核心的硬中断打断
  4. 软中断和硬中断之间,本核心的软中断可能会被本核心的硬中断打断
  5. 多个 CPU 核心之间,多个 CPU 核心除了会同时运行多个进程外,可能还会同时运行多个软中断或硬中断
    在并发状态对共享资源进行访问将出现竞争现象,从而导致最终操作结果无法预测。

竞争解决办法

  1. 进程与进程之间;可使用信号量、互斥量、自旋锁解决竞争问题(当临界区执行时间较长不推介自旋锁)。
  2. 进程和软中断之间;采用关闭软中断,并结合自旋锁解决竞争问题,在进程中关闭软中断(防止本核软中断 抢占),然后获取自旋锁(防止其他 CPU 核心抢占),在软中断中获取自旋锁(防止其他 CPU核心抢占)。
  3. 进程和硬中断之间;与进程和软中断之间类似,只是这里要关闭的是硬中断。
  4. 软中断和硬中断之间;采用自旋锁,并结合关闭硬中断解决,在软中断中关闭硬中断(防止本核硬中断抢占), 然后获取自旋锁(防止其他 CPU 核心抢占),在硬中断中获取自旋锁(防止其他 CPU核心抢 占)。
  5. 软中断与软中断之间,或硬中断与硬中断之间;此情况只存在于多核系统中,对于这种情况下的竞争问题通过自旋锁即可解决(Linux不 支持中断嵌套,即本核的软中断不能打断本核的软中断,本核的硬中断也不能打断本核的硬中断,所以解决软中断与软中断之间竞争不必关闭软中断,解决硬中断与硬中断之间竞争不必关闭硬中断)。

自旋锁常用API

//定义并初始化一个自选变量。
DEFINE_SPINLOCK(spinlock_t lock)
//初始化自旋锁
void spin_lock_init(spinlock_t *lock)
//获取自旋锁,也叫做加锁。
void spin_lock(spinlock_t *lock)
//尝试获自旋锁,如果没有获取到就返回 0
int spin_trylock(spinlock_t *lock)
//检查自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0
int spin_is_locked(spinlock_t *lock)
//释放自旋锁。
void spin_unlock(spinlock_t *lock)
//关闭软中断,并获取自动锁
void spin_lock_bh(spinlock_t *lock)
//开启软中断,并释放自旋锁
void spin_unlock_bh(spinlock_t *lock)
//关闭中断,并获取自动锁
void spin_lock_irq(spinlock_t *lock)
//开启中断,并释放自旋锁
void spin_unlock_irq(spinlock_t *lock)
//关闭中断,然后保存中断状态,并获取自旋锁
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)
//恢复中断状态,并释放自旋锁
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)

自旋锁特性:

  1. 在获取自旋锁的过程中不会引起进程休眠,所以自旋锁可以在中断中使用,而且加锁的时间要尽可能的短、在加锁过程中不可执行可能会引起进程休眠的函数。
  2. 自旋锁不可递归使用(即在同一个线程中连续两次获取同一个自旋锁),加锁和解锁必须成对使用
  3. 在使用自旋锁解决进程和中断(包括软中断)间的竞争时,在进程中必须采用 spin_lock_irqsave 和 spin_unlock_irqrestore 来进行加锁和解锁(防止进程获取到自旋锁后被中断打断,从而导致死锁)

信号量常用API

//定义一个信号量,并且设置信号量的值为 1
DEFINE_SEMAPHORE(name)
//初始化信号量 sem,设置信号量值为 val。
void sema_init(struct semaphore *sem, int val)
//获取信号量
void down(struct semaphore *sem)
//尝试获取信号量,不会进入休眠,可以在中断中使用
int down_trylock(struct semaphore *sem);
//获取信号量,但是休眠后能被信号唤醒
int down_interruptible(struct semaphore *sem)
//获取信号量,但是休眠后能被SIGKILL唤醒
int down_killable(struct semaphore *sem)
//在指定时间内获取信号量
int down_timeout(struct semaphore *sem, long jiffies)
//释放信号量
void up(struct semaphore *sem)

信号量的特性:

  1. 获取信号量时如果信号量不可用,在除 down_trylock 外都会让进程休眠进行休眠, 直到信号量可用或者超时
  2. 不可再中断中等待信号量,但是可以采用 down_trylock 尝试获取信号量
  3. 可以在中断中释放信号量
  4. 用于互斥时获取和释放必须成对使用(信号量还可以用于同步)

互斥体常用API

//定义并初始化一个互斥体
DEFINE_MUTEX(name)
//初始化互斥体
void mutex_init(mutex *lock)
//获取互斥体
void mutex_lock(struct mutex *lock)
//获取互斥体,但是休眠后能被信号唤醒
int mutex_lock_interruptible(struct mutex *lock)
//获取互斥体,但是休眠后能被SIGKILL唤醒
int mutex_lock_killable(struct mutex *lock)
//尝试获取互斥体
int mutex_trylock(struct mutex *lock)
//释放互斥体
void mutex_unlock(struct mutex *lock)
//互斥体是否被获取,如果是的话就返回 1,否则返回 0
int mutex_is_locked(struct mutex *lock)

互斥体的特性:

  1. 不可再中断中使用,包括 mutex_trylock
  2. 不可递归使用,获取和释放必须成对使用
  3. 用于互斥时消耗的资源比信号量小,效率更高
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值