zjh_larm 发表于 2010-06-02 18:20

自旋锁 可抢占内核中使用自旋锁跟使用信号量有什么区别?

自旋锁:书上说在单CPU,可抢占式内核中,自旋锁还是有用的,但是这个时候还是会发送进程上下文的切换,这个跟信号量又有什么区别呢。





自旋锁:书上说这样使用:如果进程申请获得自旋锁不成功,便一直忙等待,知道可以成功获得自旋锁为止。

如果是在SMP上,这个很好理解。但是在单CPU上,如果在忙等待的进程不放弃CPU,持有该锁的进程怎么会有机会释放该锁呢,所以后面书上讲在单CPU上自旋锁的加锁操作是空操作。但是在内核可抢占的操作系统里,自旋锁又是可用的,既然内核都可以抢占了,那就是别的进程获得了CPU,这样还是发生了进程上下文的切换,这个时候跟信号量又有什么区别了,不是很理解。

zjh_larm 发表于 2010-06-02 19:04

最新理解:

在单处理器中,自旋锁的工作仅仅是禁止内核抢占。在内核代码中,如果禁止内核抢占,内核会一直运行下去,知道它主动放弃CPU。也就是说这个时候不会发生进程切换。能够阻止该内核代码运行的就只有中断了,如果在中断中也申请同样的自旋锁就会发送死锁,所以也有一些形式的自旋锁是要禁止中断的。

我对自旋锁的理解就是:保证在内核代码中对一些数据的访问是原子进行的。

zjh_larm 发表于 2010-06-02 19:09

那么自旋锁为什么不能被阻塞呢

zjh_larm 发表于 2010-06-02 20:54

回复 3# zjh_larm


    还是自问自答。

关键问题是 申请自选锁后,内核是被禁止抢占的。

如果进程A在内核态下拥有自旋锁,被阻塞后,被调度的另外一个进程B也执行到同样的一段内核代码,申请自旋锁,但是由于进程A并没有释放该锁,所以B会一直自旋等待,同时内核被禁止抢占,也就是说进程B会一直自旋,不会让出处理器。

zjh_larm 发表于 2010-06-02 21:00

同时又考虑到一个问题。

既然在单处理器上面,使用自旋锁就是为了禁止内核抢占,那么一个中断上下文和进程上下文之间又该如何做互斥与同步呢?

我的想法是:在中断上下文中不做处理,而在进程上下文(这里指的是内核态)中调用的是spin_lock_irqsave、spin_unlock_irqrestore这一对函数,因为这对函数禁止了中断。在进程上下文在处理临界区数据的时候,不必担心中断的发生。同时进程也不用担心会被抢占,会很快的完成处理。

sara550 发表于 2010-06-04 21:48

俺也是新手,不太懂。

也许这问题太简单吧。。。建议去CSDN问,至少会有人回答你,不至于自问自答。
俺发现,新手的问题,CU里高手们大部分是根本不鸟你的
俺以前也发过几个贴(不是这个号),没一个鸟俺。。。后来就改了,有什么问题都是去CSDN问,在CU里看一些精华的讨论

sara550 发表于 2010-06-04 22:04

正在学习LKD,刚好看到地九章内核同步。
说一点我的理解阿。有不对的地方,欢迎拍砖。

1.但是在单CPU上,如果在忙等待的进程不放弃CPU,持有该锁的进程怎么会有机会释放该锁呢
我的理解是:申请自旋锁失败后,不断忙循环等待,直到该进程的时间片用完,时间片用完,就必须切换进程了。

2.但是在内核可抢占的操作系统里,自旋锁又是可用的
为了支持内核抢占,每个进程的thread_info引入了preempt_count计数器。该计数器初始值为0,每当使用锁的时候值加1,释放锁的时候值减1。当该值为0时,内核就可以抢占。
所以,如果你使用了自旋锁,那么preempt_count的值就不为0,此时不允许内核抢占。

3.如果进程A在内核态下拥有自旋锁,被阻塞后,被调度的另外一个进程B也执行到同样的一段内核代码,申请自旋锁,但是由于进程A并没有释放该锁,所以B会一直自旋等待,同时内核被禁止抢占,也就是说进程B会一直自旋,不会让出处理器。
我的理解:既然是进程,时间片到了就切换进程。

4.那么一个中断上下文和进程上下文之间又该如何做互斥与同步呢
ISR与下半部都会抢占进程上下文,ISR还会抢占下半部,所以:
1>进程上下文与ISR:若共享数据,则在进程上下文中加锁的同时还要禁止本地中断。
2>进程上下文与下半部:若共享数据,则在进程上下文中加锁的同时还要禁止下半部执行。
3>ISR与下半部:若共享数据,则在下半部中加锁的同时还要禁止本地中断。

sara550 发表于 2010-06-04 22:08

自旋锁与信号量的不同
1.自旋锁在申请锁失败后,不断忙循环等待锁可用
2.信号量在申请失败后,把自己放到等待队列,然后睡眠。

所以,自旋锁可以用在ISR中,而ISR中不可以使用信号量。

sara550 发表于 2010-06-04 22:16

3.如果进程A在内核态下拥有自旋锁,被阻塞后,被调度的另外一个进程B也执行到同样的一段内核代码,申请自旋锁,但是由于进程A并没有释放该锁,所以B会一直自旋等待,同时内核被禁止抢占,也就是说进程B会一直自旋,不会让出处理器。
刚又看了下书,书上说是:如果不支持内核抢占,内核代码就一直执行,知道完成为止。
所以。。。俺也懵了,不懂。

sara550 发表于 2010-06-04 22:36

关于内核抢占,Robert Love在LKD原文中是这么写的:

Kernel Preemption
The Linux kernel, unlike most other Unix variants and many other operating systems, is a fully preemptive kernel. In non-preemptive kernels, kernel code runs until completion. That is, the scheduler is not capable of rescheduling a task while it is in the kernelkernel code is scheduled cooperatively, not preemptively. Kernel code runs until it finishes (returns to user-space) or explicitly blocks. In the 2.6 kernel, however, the Linux kernel became preemptive: It is now possible to preempt a task at any point, so long as the kernel is in a state in which it is safe to reschedule.
So when is it safe to reschedule? The kernel is capable of preempting a task running in the kernel so long as it does not hold a lock. That is, locks are used as markers of regions of non-preemptibility. Because the kernel is SMP-safe, if a lock is not held, the current code is reentrant and capable of being preempted.
The first change in supporting kernel preemption was the addition of a preemption counter, preempt_count, to each process's thread_info. This counter begins at zero and increments once for each lock that is acquired and decrements once for each lock that is released. When the counter is zero, the kernel is preemptible. Upon return from interrupt, if returning to kernel-space, the kernel checks the values of need_resched and preempt_count. If need_resched is set and preempt_count is zero, then a more important task is runnable and it is safe to preempt. Thus, the scheduler is invoked. If preempt_count is nonzero, a lock is held and it is unsafe to reschedule. In that case, the interrupt returns as usual to the currently executing task. When all the locks that the current task is holding are released, preempt_count returns to zero. At that time, the unlock code checks whether need_resched is set. If so, the scheduler is invoked. Enabling and disabling kernel preemption is sometimes required in kernel code and is discussed in Chapter 9.
Kernel preemption can also occur explicitly, when a task in the kernel blocks or explicitly calls schedule(). This form of kernel preemption has always been supported because no additional logic is required to ensure that the kernel is in a state that is safe to preempt. It is assumed that the code that explicitly calls schedule() knows it is safe to reschedule.
Kernel preemption can occur
When an interrupt handler exits, before returning to kernel-space
When kernel code becomes preemptible again
If a task in the kernel explicitly calls schedule()
If a task in the kernel blocks (which results in a call to schedule())
页: [1] 2 3
查看完整版本: 自旋锁 可抢占内核中使用自旋锁跟使用信号量有什么区别?