LDD3第五章的学习-并发和竞态

作者:Aningsk ,本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。
 

并发和竞态,如果只是描述一下它们,的确不是很复杂:内核中的信号量和自旋锁,就是为了解决:共享资源可能会在同一时间被不同的线程修改,而导致不一致的问题。方法就是加一个标志变量,有进程在访问共享资源时,这个变量置位,其他线程看到这个变量已经置位,就不再访问共享资源。如此就解决了并发和竞态的问题。那个标志变量就是信号量或自旋锁。

但是这个问题在真正处理时,纠缠起来也挺麻烦的,比如说死锁。

 

临界区:就是操作共享资源的代码,在任意时刻,这里的代码只能被一个线程执行。

 

信号量:进入临界区前减一,离开临界区后加一(PV操作)。而使用信号量前,其中具体的数值,是代表了可以同时访问临界区的线程数量;如果这个数值初始化时被设置为1,那在某一时刻只有一个线程可以访问临界区,即达到了互斥的目的,这就是互斥体了。也就是说互斥体是信号量的一种。内核提供初始化互斥体的宏其实是对初始化信号量接口的封装。

PV操作,在Linux中,P操作对应的是down(减一);V操作对应的是up(加一)。我记不住PV的名字,据说这是荷兰语的首字母,本人外语只会英语,记住down和up就够用。

对于down这个操作,Linux提供了三种版本:down(临界区的访问不可中断),down_interruptible(临界区的访问可以被中断),down_trylock(不能获得信号量直接返回,而不是休眠)。

当一个线程希望获得信号量时,如果可以获得,那它直接获得这个临界区的执行权力;其他线程如果想再获得信号量访问临界区,则这个后来的线程就会休眠(如果获取信号量使用的是down_trylock则不会休眠),直到前一个拥有者释放信号量,休眠的线程醒来,去尝试获得信号量。

访问临界区可中断的版本几乎是我们始终要使用的(down_interruptible)。因为如果使用不可中断的,则会造成一个不可杀死的进程。我理解的是:杀掉一个进程是向它发送信号,这对进程来说是一个中断,如果这个进程以不可中断的方式取得了信号量,它就不能收到杀死进程的信号了。

在P115有down_interruptible使用例子。这个函数在操作过程中被中断的话,会返回非零值,调用者也不会拥有该信号量,所以要检查返回值。其实做起来也挺简单的,非零的话,直接return –ERESTARTSYS剩下的任务或者是重试或者是撤销,都由内核帮我们做。

 

完成量:直接和信号量比较一下吧。

  1. 信号量:一个任务完成,up对应的信号量。比如写完成,唤醒读进程。而一般来说只能有一个进程可再次取得信号量;若是存在众多的读进程,众多的竞争只有一个胜出者,这样会很影响读的性能。

  2. 完成量:一个任务完成,它会去唤醒所有等待此任务完成的进程。比如写完成,所有的等待读都会唤醒,一块来读。对一个共享资源众多的读是没有问题的。完成量适用于这种情况,并不影响读的性能。

    这似乎和读写信号量差不多,但它不仅仅限于读写。"模块退出时的内核线程终止"没看懂怎么弄的。

    同时,在一些时间比较长较复杂的任务,信号量可能会有问题,如书中P117说的"受竞态影响""提前消失"等。

 

自旋锁:首先自旋锁中不能有休眠(引起休眠的操作很多,获取信号量就会引起休眠)。进程在想要获取自旋锁时,会忙等,并不释放CPU;而不像信号量,会阻塞休眠然后调度别的进程。

还是拿信号量做比较:

  1. 信号量:对于一个CPU上某一时刻的进程,它在等待信号时会休眠(或说阻塞)。这个CPU会把这个进程调度出去,然后CPU会去处理其他工作。

  2. 自旋锁:对于一个CPU上某一时刻的一个进程,它在等待自旋锁时,会不断尝试取得这个锁而不是休眠。这个CPU也不会去做其他工作。

    自旋锁最初是为了在多处理器系统上使用的。要注意的是:只要调用spin_lock,不管是获得了锁,还是在等待锁,这个调用都会首先关掉内核抢占。这意味着此线程会一直占有当前CPU,直到获得自旋锁,运行临界区结束,最后释放自旋锁,在这些之后CPU才会恢复抢占,执行其他任务。所以,如果在上面过程中出现问题(比如意外的调度),没能正确释放锁,至少这个CPU是会挂起了,结果很可能是系统死锁。

    单处理器非抢占内核:自旋锁在编译时被忽略。

    单处理器抢占内核:自旋锁仅仅被当作一个设置内核抢占的开关。

    多处理器:此时完全发挥自旋锁的作用,自旋锁在内核中防止多处理器中并发访问临界区,防止内核抢占造成竞态。

使用自旋锁必须保证:一、临界区内的代码不会造成休眠,或者说进程调度,自旋锁保护的临界区不能以任何原因放弃处理器,而且大多数是要禁止中断(spin_lock就会禁止掉中断)。二、只要持有自旋锁或获取自旋锁,该处理器的抢占就会被禁止,所以自旋锁的持有时间要尽可能短。

 

使用锁的规则:一、不论是信号量还是自旋锁,都不允许锁拥有者第二次获得这个锁;否则,系统将挂起。二、使用锁的难处不是锁的API用法之类的问题,而是使用锁的设计规则,即锁是保护谁,在什么情景下要加锁。一般来说,"系统调用直接使用的函数均要获得信号量,以便保护要访问的设备结构。而其他的内部函数只会由驱动中其他的函数调用,则假定信号量已经被正确获得。"三、尽量不要拥有多个锁,在必须获取多个锁时,应该始终以相同的顺序获得。

 

seqlock:这个东西允许读取者对资源的自由访问,但需要读取者检查是否和写入者发生冲突,如果有冲突,就需要重新读取。在P129有它的用法示例。

 

书中别的原子变量、位操作等等就不说了吧。

先写到这里咯~

 

Aningsk

2014-12-25

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值