Linux kernel 同步策略的应用

来源:互联网 发布:mysql 特殊字符 编辑:程序博客网 时间:2024/06/03 17:33

l  下半部可以抢占进程上下文的代码,所以,当进程在访问和下半部共享的数据时,应首先获得锁,同时禁止下半部的执行。

l  中断可以抢占下半部,所以,当下半部在访问和中断共享的数据时,应首先获得锁,同时禁止中断。

l  即使同类的软中断也有可能在不同的cpu上并行执行,所以,软中断中用到的任何共享的数据在使用前都需要加锁。

l  同类tasklet不会并发执行,所以,同类tasklet用到的共享数据不需要加锁。但不同类的tasklet在使用共享数据前需要加锁。这里提到的“同类”就是指“同一个”的意思。

l  内核抢占:如果一个内核中的一个自旋锁被持有,那么这时是SMP_safe preempt_safe(因为这时会disable内核抢占)。但有些数据是SMP_safe的情况下(每个cpu一份),本不需要加锁,这时候这些数据就是preempt_dangerous的,这时需要手动的preetmpt_disable

l  如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bhspin_unlock_bh来保护。当然使用spin_lock_irqspin_unlock_irq以及spin_lock_irqsavespin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bhspin_unlock_bh是最恰当的,它比其他两个快

l  如果被保护的共享资源只在进程上下文和tasklettimer上下文访问,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklettimer是用软中断实现的

l  如果被保护的共享资源只在一个tasklettimer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklettimer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。

l  如果被保护的共享资源只在两个或多个tasklettimer上下文访问,那么对共享资源的访问仅需要用spin_lockspin_unlock来保护,不必使用_bh版本,因为当tasklettimer运行时,不可能有其他tasklettimer在当前CPU上运行如果被保护的共享资源只在一个软中断(tasklettimer除外)上下文访问,那么这个共享资源需要用spin_lockspin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。

l  如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lockspin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。

l  如果被保护的共享资源在软中断(包括tasklettimer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irqspin_unlock_irq来保护对共享资源的访问。而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lockspin_unlock来保护对共享资源的访问就可以了。因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irqspin_unlock_irq来保护对共享资源的访问。

l  在使用spin_lock_irqspin_unlock_irq的情况下,完全可以用spin_lock_irqsavespin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些,因为它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsavespin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irqspin_unlock_irq最好。

l  需要特别提醒,spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问。

 

Q: softirq的执行可以嵌套吗?或者softirq在同一个cpu上会被另一个softirq抢断吗?

A: No. do_softirq()负责执行数组softirq_vec32]中设置的软中断服务函数。每个CPU都是通过执行这个函数来执行软中断服务的。由于同一个CPU上的软中断服务例程不允许嵌套,因此,do_softirq()函数一开始就检查当前CPU是否已经正出在中断服务中,如果是则 do_softirq()函数立即返回。举个例子,假设CPU0正在执行do_softirq()函数,执行过程产生了一个高优先级的硬件中断,于是 CPU0转去执行这个高优先级中断所对应的中断服务程序。总所周知,所有的中断服务程序最后都要跳转到do_IRQ()函数并由它来依次执行中断服务队列中的ISR,这里我们假定这个高优先级中断的ISR请求触发了一次软中断,于是do_IRQ()函数在退出之前看到有软中断请求,从而调用do_softirq()函数来服务软中断请求。因此,CPU0再次进入do_softirq()函数(也即do_softirq()函数在CPU0上被重入了)。但是在这一次进入do_softirq()函数时,它马上发现CPU0此前已经处在中断服务状态中了,因此这一次do_softirq()函数立即返回。于是,CPU0回到该开始时的do_softirq()函数继续执行,并为高优先级中断的ISR所触发的软中断请求补上一次服务。从这里可以看出,do_softirq()函数在同一个CPU上的执行是串行的。 

 

Q: preempt_disable会禁止抢断,那么会禁止中断抢断吗?

A: No.preempt_disable仅仅是禁止process之间的抢断,并不能禁止中断对process的抢断,由于中断do_irq返回前要do_softirq,所以,也不能禁止softriqprocess的抢断。另外,禁止抢占意味着即使时间片到了调度器依然会让其继续运行。

 

Q: RCU中说的CPU发生了上下文切换称为经历一个quiescent state,这个上下文切换包括中断上下文和sofrirq上下文吗?

A: 如果写者用的是call_rcu(),这时的一个quiescent state就是指一次process上下文切换,处于进程上下文中的读者就需要用rcu_read_lock()

如果写者用的是call_rcu_bh(),这时的一个quiescent state就是指一次completion of a softirq handler,处于进程上下文中的读者就需要用rcu_read_lock_bh()(不但禁止被其他进程抢断,同时禁止被softirq抢断)。

另外处于中断中的读者只需要用rcu_read_lock()就好了,因为中断不会被softirq,更不会被process抢断。实际上中断和softirq是没有上下文的,他们是靠抢占process的上下文过活。

 

Q: 为什么kernel中大的Route cachecall_rcu_bh?

A: 根据call_rcu_bh的注释,当大多数读者是在softirq上下文工作的时候,推荐写者用call_rcu_bh,读者用read_rcu_bh。因为bh版本的quiescent state时间更短,可能会对Performance有好处。

Q: preempt_disable 后,时间片到了,该线程会被移出而调度其他线程吗?

II)内核抢占:

注:增加一个标志preempt_count表示当前运行任务(current)所持有锁的数量。

1 中断处理程序返回内核空间时检查如果need_resched标志被设置,并且preempt_count标志值为0,则启用调度器;

2 当前进程执行释放锁的代码会检查preempt_count值若为0(没有持有锁了)并且设置了need_resched标志,则启用调度器;

3 内核显示调用schedule()函数;

注:可以使用preempt_disable()/preempt_enable()函数对禁止和允许内核抢占。

http://blog.csdn.net/jiyiqinlovexx/article/details/46582763
原创粉丝点击