背景:内核同步问题,多个进程(线程)同时访问和操作共享资源时,就有可能发生各个线程之间相互覆盖共享数据的情况,造成被访问数据处于不一致状态,并发访问共享数据通常是不安全的,是造成系统不稳定的一类隐患。
临界区:就是访问和操作共享数据的代码段
竞争条件:如果两个执行线程有可能处于同一个临界区中同时执行
原子操作:指令以原子的方式执行,即不可分割开的操作;该操作一定是在同一个cpu时间片中完成,这样即使线程被切换,多个线程也不会看到同一块内存中不完整的数据。
i++不是原子操作:1)从内存读数据到寄存器,2)寄存器自增,3)写回内存
锁机制:锁是采用原子操作的,而原子操作不存在竞争
Linux 内核中造成并发执行的原因:中断、内核抢占、睡眠以及用户空间的同步、对称多处理器同时访问同一共享资源。
对于程序员来讲,困难的不是使用锁,而是找到真正需要共享的数据和相应的临界区。
同步:多个进程之间时直接依赖关系,具有一定的执行顺序。
互斥:多个进程之间时间接依赖关系,对系统的某些共享资源,一次只允许一个线程访问。
内核信号量:信号量是一种睡眠锁,如果有一个任务试图获得一个不可用(已经被占用)的信号量时,信号量会将其推进一个等待队列,然后让其睡眠(内核中唯一允许睡眠的锁)。当持有的信号量可用(被释放)后,处于等待队列中的那个任务将被唤醒,并获得该信号量。信号量分为计数信号量(允许在一个时刻至多有多个锁持有者,不能进行强制互斥,因为它允许多个进程 同时访问临界区,不常用)和二值信号量。
区别于用户态用户进程通信机制中的信号量:POSIX 信号量和SYSTEM V信号量。
参考http://blog.csdn.net/qinxiongxu/article/details/7830537
互斥体(mutex):指的是任何可以睡眠的强制互斥锁,其行为与使用计数为1的信号量类似。
1.任何时刻只有一个任务可以持有mutex;
2.给mutex上锁者必须负责给其再解锁;
3.递归地上锁和解锁是不允许的;
4.mutex不能在中断或者下半部中使用;
5.当持有一个mutex时,进程不可以退出;
自旋锁(spin lock):最多只能被一个可执行线程持有,如有一个执行线程试图获得一个被已经持有(即所谓的争用)的自旋锁,那么该线程就会一直进行循环-旋转-等待(cpu 浪费在忙等待中)锁重用。
自旋锁是不可以递归的!!!:当一个线程试图得到一个自己真持有的锁,必须自旋,等待自己释放这个锁,但是处于自旋锁等待中,所以该线程永远没有机会释放锁,于是被自己锁死了。
自旋锁适合场景:加锁时间不长并且代码不会休眠(比如中断处理程序)
互斥体、信号量、自旋锁的比较
1.互斥体与信号量:一般条件下,选择互斥体,信号量更偏向复杂、很底层的设计
2.互斥体与自旋锁
中断上下文加锁、低开销、短时间锁定------->自旋锁
长期加锁、持有锁需要睡眠--------->互斥体