一、内核并发原因:
1)中断:由于中断可以打断当前正在执行的代码异步发生
2)软中断和tasklet:内核可以打断当前正在执行的代码,在任何时刻唤醒或者调度软中断和tasklet
3)内核抢占:被其它内核任务抢占
4)睡眠、用户空间同步:内核执行的进程睡眠引起程序调度
5)对称多处理器:多个处理器同时并发执行代码
中断安全代码(interrupt-saft):中断处理程序中避免并发访问的安全代码。
SMP安全代码(SMP-safe):对称多处理器的机器中能避免并发访问的安全代码。(CONFIG_SMP配置选项控制内核是否支持SMP,若单处理器可以选择不下载)
抢占安全代码(preempt-safe):在内核抢占时避免并发访问的安全代码。
锁的争用:高度争用的锁会成为系统的瓶颈,降低系统的性能。
扩展性:细粒度
二、内核同步方法
1、原子操作
内核提供:
对整数的原子操作接口:对atomic_t类型的数据进行处理,操作声明在
位操作的原子操作接口:定义在
2、自旋锁
1)原理:同一时刻至多被一个可执行线程持有,其它线程请求时忙等。不能被长时间占有,在短时间内进行轻量级加锁。
2)实现方法:定义在文件
注意:
自旋锁不能递归。自旋锁是多处理器防止并发的保护机制,单处理器时则被当做内核抢占机制是否启用的开关。
中断处理程序中,在获取锁之前要禁止本地中断,避免双重请求死锁。spin_unlock_irqsave保存当前中断状态并禁止本地中断,store解锁。
下半部和进程上下文共享数据时候必须对上下文进行保护,加锁时需要禁止下半部执行;
中断可以抢占下半部执行 ,因此若共享数据,下半部还需要禁止中断;
不同类的共享数据的tasklet和多处理器上共享数据的软中断也许彼此注意锁的使用。
3)读、写自旋锁(共享、并发/排斥锁)
原理:多个读任务可以并发持有读者所,写的锁只能被一个写任务持有。
对读者更有利,因为写锁会等待所有读锁执行完毕。
3、信号量
睡眠锁,分为互斥信号量和计数信号量。
使用情况:锁被长时间持有,只能在进程上下文中获取信号量锁(中断上下文不能调度),可以在持有信号量时睡眠,因为可以睡眠所以不能占用自旋锁。
操作:P、V操作(down、up)
1)创建和初始化
semaphore类型,定义在
2)使用
down_interruptible 获取指定信号量,如果不可用则设置进程为TASK_INTERRUPTABLE睡眠;
down_trylock以阻塞方式获取指定信号量,立即返回获取结果;
up释放指定信号量。
3)读写信号量
rw_semaphore类型,定义在
4、互斥体
mutex:任何可以睡眠的强制互斥锁。
在linux中为类似计数为1的信号量并提供了简洁的操作接口,使用有较多限制。
使用情况:一般优先使用互斥体,当限制条件不能满足,再考虑使用信号量。
5、完成变量
completion,定义在
声明初始化之后,在指定的完成变量上,需要等待的任务调用wait_for_completion等待指定事件,发生事件的任务调用completion发送信号唤醒等待任务。
6、顺序锁
用于读写共享数据,通过序列计数器实现,对写者更有利。
适用情况:读者多、写着少,写优于读,数据简单。
7、屏障
操作以预定顺序执行:
rmb 读内存屏障;wmb 写内存屏障 ; mb两方等;barrier 组织编译器跨屏障对载入或者存储操作进行优化。