Linux内核设计与实现 第九章 内核同步与介绍

9.1临界区与竞争条件

临界区:访问和操作共享数据的代码段
多个线程并发访问一个资源通常是不安全的,为了避免在临界区中并发访问,编程者必须保证这些代码原子地执行。也就是说操作在执行结束前不可被打断。
竞争条件是一个bug,是两个执行线程处于同一个临界区时,两个执行线程同时执行了(要解决bug必须2选1,也就出现了竞争)。
同步是避免并发和防止竞争条件

1)为什么临界区需要保护

在这里插入图片描述
在这里插入图片描述

2)单个变量

在这里插入图片描述

9.2加锁

硬件:体系结构会提供一些实现算术和比较之类的原子操作。
软件:Linux内核程序员设计了锁机制。
在这里插入图片描述
加锁开锁硬件提供了原子操作。避免加锁开锁时产生竞争。

1)造成并发执行的原因

a)用户空间伪并发:
如果进程A进入了临界区,此时调度进程B抢占进程A,进程B随后进入了与进程A相同的临界区,进程A与B相互之间就会产生竞争。
信号处理是异步的,如果进程A进入了临界区,此时产生了信号需要马上处理,处理函数刚好要访问与进程A相同的临界区,进程A与信号相互之间就会产生竞争。
以上两种情况实际只是交叉执行,所以是伪并发。

b)用户空间真并发:
如果一台支持对称多处理器的机器,那么进程就可以正真地在临界区中同时执行

c)内核中有类似可能造成并发执行的原因,它们是:
中断----中断几乎可以在任何时刻异步发生,也就可能随时打断当前正在执行的代码
软中断和tasklet----内核能在任何时刻唤醒或调度软中断和tasklet,打断当前正在执行的代码
内核抢占----因为内核具有抢占型,所以内核中的任务可能会被另一任务抢占
睡眠及与用户空间的同步----在内核执行的进程可能会睡眠,这就会唤醒调度程序,从而导致调度一个新的用户进程执行
对称多处理----两个或多个处理器可以同时执行代码
真正困难的就是发现上述的潜在并发执行的可能,并有意识地采取某些措施来防止并发执行。

2)了解要保护些什么

a)找出哪些数据需要保护是关键所在:
线程中的局部数据仅仅被它访问,显然不需要保护。还有动态分配的数据结构,其地址在堆栈中。也不需要加锁
大多数内核数据结构都需要加锁
如果有其他执行线程可以访问这些数据,那么就给这些数据加上某种形式的锁。
如果任何其他什么东西都能看到它,那么就要锁住它。记住:要给数据而不是给代码加锁。

b)在编写代码时,你要问自己下面这些问题:
这个数据是不是全局的?除了当前线程外,其他线程能不能访问它。
这个数据会不会在进程上下文和中断上下文中共享?它是不是要在两个不同的中断处理程序中共享?
进程在访问数据时可不可能被抢占?被调度的新程序会不会访问同一数据?
当前进程是不是会睡眠(阻塞)在某些资源上,如果是,它会让共享数据处于核中状态?
怎样防止数据失控?
如果这个函数又在另一个处理器上被调度将会发生什么呢?
如何确保代码原理并发威胁呢?
简而言之,所有内核全局变量和共享数据都需要某种形式的同步方法。

9.3死锁

a)自死锁和ABBA死锁
在这里插入图片描述
b)预防死锁
在这里插入图片描述

9.4争用和扩展性

锁的争用:简称锁争用,是指当锁被占用时,有其他线程试图获得该锁。
加锁粒度:用来描述加锁保护的数据规模。
如何可以被计量的计算机组件都可以涉及可扩展性。

当锁争用严重时,加锁太粗会降低可扩展性;当锁争用不明显时,加锁过细会加大系统无畏开销。
即当 试图获得某线程占用的锁 的线程数量非常多时,加锁保护的数据规模太大会降低可扩展性,就是例如扩展了几个处理器,性能提高效果很小。
当 试图获得某线程占用的锁 的线程数量非常少时,加锁保护的数据规模太小会加大系统无畏开销,就是处理器数量少,单位时间产生并发访问的次数少,加锁保护的数据规模太小导致产生了很多的锁,很多锁没有用于应对并发,只是无畏的执行了锁操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值