《Linux内核设计与实现》学习【5】—— 内核同步

1 基本概念

  临界区:访问和操作共享数据的代码段
  竞争条件:多个执行线程处于同一临界区中同时执行
  同步:避免并发和防止竞争条件

2 造成并发执行的原因

  (1)中断:中断随时会发生,也就会随时打断当前执行的代码。
  (2)软中断和tasklet:软中断和tasklet也会随时被内核唤醒执行,也会像中断一样打断正在执行的代码
  (3)内核抢占:内核具有抢占性,发生抢占时,如果抢占的线程和被抢占的线程在相同的临界区,就产生了竞争条件
  (4)睡眠及用户空间的同步:用户进程睡眠后,调度程序会唤醒一个新的用户进程,新的用户进程和睡眠的进程可能在同一个临界区中
  (5)对称多处理:2个或多个处理器可以同时执行相同的代码
  注意:要给数据加锁而不是给代码加锁

3 死锁

(1)产生原因
  一个或多个执行线程和一个或多个资源,每个线程都在等待其中一个资源,但所有资源已经都被占了。所有线程相互等待,但他们永远不会释放已经占有的资源。
(2)避免死锁
  1)按顺序加锁,使用嵌套的锁时,必须保证以相同的顺序获取锁。
  2)防止发生饥饿。即设置一个超时时间,防止一直等待下去。
  3)不要重复请求同一个锁。
  4)设计应力求简单。加锁的方案越复杂就越容易出现死锁。

4 内核同步方法

4.1 原子操作

  atomic_t类型定义在<linux/types.h>
  32位:atomic_t
  64位:atomic64_t

4.2 自旋锁

  自旋锁(spin lock),只能被一个线程持有,某个线程试图获取锁,该线程会忙-旋转-等待。
  基本使用:

DEFINE_SPINLOCK(mr_lock);
spin_lock(&mr_lock);
//临界区
spin_unlock(&mr_lock);

注意:
  1)可用在中断处理程序中
  2)在获取锁前,首先禁止当前处理器上的本地中断

4.3 读写锁

  多个读任务可以并发地持有锁,只能有一个任务可以持有写锁
  使用

DEFINE_RWLOCK(mr_rwlock);

read_lock(&mr_rwlock);
/* 临界区(只读).... */
read_unlock(&mr_rwlock);

write_lock(&mr_lock);
/* 临界区(读写)... */
write_unlock(&mr_lock);

4.4 信号量

  是一种睡眠锁,当任务不能获取信号量时,睡眠。
  适用于长时间持有的情况
  两种:计数信号量、二值信号量

/* 定义并声明一个信号量,名字为mr_sem,用于信号量计数 */
static DECLARE_MUTEX(mr_sem);

/* 试图获取信号量....*/
if(down_interruptible(&mr_sem)){
	...
}

/* 临界区 ... */

/* 释放给定的信号量 */
up(&mr_sem);

4.5 读写信号量

  与读写自旋锁类似。

4.6 互斥体

  类似二值信号量。
  相对信号量,优先选择互斥体。
  自旋锁和互斥量

需求建议的加锁方法
低开销加锁优先使用自旋锁
短期锁定优先使用自旋锁
长期加锁优先使用互斥体
中断上下文中加锁使用自旋锁
持有锁需要睡眠使用互斥体

4.7 完成变量

  一个任务执行工作,另一个任务在完成变量上等待,类似信号量。
  方法

方法描述
init_completion(struct completion *)初始化指定的动态创建的完成变量
wait_for_completion(struct completion *)等待指定的完成变量接受信号
complete(struct completion *)发信号唤醒任何等待任务

4.8 BLK:大内核锁

  不鼓励使用,部分代码中依然沿用。
  特性:
  1)持有BLK的任务可以睡眠
  2)递归锁
  3)只可以用在进程上下文中

4.9 顺序锁

  读锁被获取时,写锁仍然可以被获取。依靠一个序列计数器,当有疑义的数据被写入时,序列值增加。
  使用

//定义
seqlock_t mr_seq_lock = DEFINE_SEQLOCK(mr_seq_lock);

//写
write_seqlock(&mr_seq_lock);
/*写锁被获取*/
write_sequnlock(&mr_seq_lock);

//读
unsigned long seq;
do
{
    seq = read_seqbegin(&mr_seq_lock);
	...
} while(read_seqretry(&mr_seq_lock, seq)); 

  适用:数据存在多个读者,写者很少,希望写优先于读,数据简单

4.10 禁止抢占

  对于SMP,某些情况不需要自旋锁,但仍需要关闭内核抢占。

preempt_disable()
/*抢占被禁止*/
preempt_enable()

4.11 顺序和屏障

  目的:确保指令顺序执行,不被处理器重排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值