Linux内核同步

为什么需要同步?

并发访问共享数据可能会造成数据的不一致、相互覆盖等问题,是导致系统不稳定的隐患,但是这种问题又很难在调试中发现,所以对于共享资源要小心谨慎,需要同步。

并发执行是造成竞争条件的基础,中断、软中断、tasklet、内核抢占、多处理器等都会形成并发执行。

死锁:一个或多个线程和一个或多个资源,每个线程都在等待其中一个资源,但所有的资源都已被占用,造成所有的线程都在等待,无法运行。

同步方法

原子操作

原子操作可以保证指令以原子的方式执行(执行过程不会被打断)。

原子整数操作

atomic_t 类型的数据 是Linux内核里面声明为原子类型的整数变量,同时定义了一系列内联函数,用于操作atomic_t数据,加减等操作。

对于64位系统,定义了atomic64_t类型。

自旋锁 spinlock

自旋锁最多只能被一个可执行线程持有,如果一个执行线程试图获得一个已经被占用的自旋锁,那么该线程就会一直忙循环,直到锁可用。忙循环是无意义的浪费计算资源,所以持有自旋锁的时间应该尽可能的短。

自旋锁不可递归!

中断处理中也可以使用自旋锁,但是在获得自旋锁之前一定要禁止本地中断,否则,中断程序可能被中断,如果获取同一个自旋锁,就会死锁。

DEFINE_SPINLOCK(my_spinlock);
unsigned long flags;
spin_lock_irqsave(&my_spinlock, flags);
...
spin_unlock_irqrestore(&my_spinlock, flags);

内核配置选项CONFIG_DEBUG_SPINLOCK为使用自旋锁的代码加入许多调试检测手段。用以检查自旋锁是否正常。

读写自旋锁

对于某些数据结构,可能有多个线程要对其进行读写。读写锁是为了并发读,排斥写而生的。

多个线程可以并发的持有读锁,但是对于写操作,只能有一个线程,且此时不能有线程持有读锁。如果在请求写锁时,有读锁被持有,则自旋等待,直到读锁都被释放。

信号量

Linux中的信号量是一个睡眠锁。当进程想要获得某个信号量时,这个信号量是不可用的,那么这个进程就进入等待队列,让出CPU资源。直到该信号量可用,唤醒等待队列中的进程。

相较于自旋锁,信号量适合于锁会被长时间持有的情况。持有信号量的进程会被抢占,而持有自旋锁的进程不会被抢占。

互斥信号量:计数等于1,只允许一个进程持有该锁。down(), up()操作获得释放锁。

互斥体

mutex与互斥信号量很类似,只是更加简洁高效。它也会把未获得锁的进程睡眠。

RCU

Read-Copy Update,适用于频繁读数据,而修改数据并不多的场景。
宽限期:一个线程要修改数据,要等到所有读线程都读取完成后,才能更新数据,这个过程叫宽限期。
自旋锁是互斥的,同一时刻只能有一个线程进入临界区,读写自旋锁性能要好一些,允许多个读并发,但是updater不能同时执行。RCU允许一个updater(需要spin lock保证)和多个reader并发执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值