手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>网站运营>建站经验>列表

详谈Linux 2_4_x内核信号量机制

来源:互联网 作者:west263.com 时间:2008-04-16
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

count值为0,有一个进程企图获得该信号量。
程式会在第3行对count进行减1操作,结果为-1,第4行判断发现count值小于0,所以跳至第7行执行__down_failed,进而在第16 行执行__down函数。在__down函数中,首先声明睡眠队列,然后加锁,这时sleepers的值为初始值0,表示在这之前没有进程睡眠在这个信号 量上,通过对这个值加1[第27行],然后又减1[第35行]的操作后,将这个值加回到count中[第35行],结果count当然仍为-1,所以跳至 第39行将sleepers的值设为1,表示有进程在睡眠队列,sleepers这个值本身也是为下一次将他加回到count中做好准备。最后解锁,睡 眠,等待被唤醒。

场景三:
count值为-1,有一个进程企图获得该信号量。
程式会在第3行对count进行减1操作,结果为-2,第4行判断发现count值小于0,所以跳至第7行执行__down_failed,进而在第16 行执行__down函数。在__down函数中,首先声明睡眠队列,然后加锁,这时sleepers的值肯定为1(因为count值为-1,表示有进程睡 眠在这个信号量上),通过对这个值加1[第27行],然后又减1[第35行]的操作后,将这个值加回到count中[第35行],结果为-1,这里等于将 上个进程欠着count的,在sleepers中的那个值还给了count,但这个进程还得欠着,所以跳至第39行将sleepers的值设为1,表示有 进程在睡眠队列,并为下一次将这个值加回到count中做好准备。最后解锁,睡眠,等待被唤醒。

场景四:
count值为1,有两个进程同时企图获得该信号量。
当进程A在第3行对count进行减1操作后,进程B又执行到第3行,并也对count进行减1操作,使其值为-1,但进程A在第4行仍旧会判断发现 count值大于等于0,所以在第5行直接返回。这是因为第5行只是根据当前CPU的标志寄存器进行判断,而进程B所在的CPU执行的结果,并不会影响到 进程A所在的CPU的标志寄存器。进程B以后的执行过程就相当于场景二。

场景五:
count值为0,有两个进程同时企图获得该信号量。
当进程A在第3行对count进行减1操作后,进程B又执行到第3行,并也对count进行减1操作,使其值为-2,进程A在第4行判断发现count值 小于0,所以跳至第7行执行__down_failed,进而在第16行执行__down函数。在__down函数中,首先声明睡眠队列,然后加锁。这时 进程B再运行,将被阻止在加锁语句上[第26行]。进程A发现sleepers的值为初始值0,表示在这之前没有进程睡眠在这个信号量上,通过对这个值加 1[第27行],然后又减1[第35行]的操作后,将这个值加回到count中[第35行],结果仍为-2,所以跳至第39行将sleepers的值设为 1,表示有进程在睡眠队列,并为下一次将这个值加回到count中做好准备。最后解锁,睡眠,等待被唤醒。由于进程A解锁,所以进程B得以继续运行,进程 B发现sleepers的值为1,表示有进程睡眠在这个信号量上,通过对这个值加1[第27行],然后又减1[第35行]的操作后,将这个值加回到 count中[第35行],结果为-1,仍不为0,所以跳至第39行将sleepers的值设为1,表示有进程在睡眠队列,并为下一次将这个值加回到 count中做好准备。最后解锁,睡眠,等待被唤醒。

场景六:
count值为-1,有两个进程同时企图获得该信号量。
这个场景分析过程同场景五,结果依然是count值为-1,sleepers为1,这两个进程进入睡眠状态,等待被唤醒。

对于count值其他负值的情况,其实是上述的某个场景未进行到稳定状态的结果,分析过程能够归并到上述的某个场景中。通过以上场景分析,我们能够总结出,稳定状态的结果实际上只有两种情况:
1.count大于等于0,sleepers为0,没有进程睡眠在等待队列中。
2.count为-1,sleepers为1,有进程睡眠在等待队列中。

下面我们将up函数的执行,也插入到场景中来。
首先分析一下up()函数,他的实现就简单了,up()利用汇编原子地将count加1,假如小于等于0表示有等待进程(大多数情况结果是0或正数,判断 “小于等于0”而不是“等于0”的原因,是因为结果有可能是负数),则调用__up_wakeup(),进而调用__up()唤醒等待进程,否则直接返 回。
(include/asm-i386/semaphore.h):
static inline void up(struct semaphore * sem)
{
66 __asm__ __volatile__(
67 "# atomic up operation\n\t"
68 LOCK "incl %0\n\t" /* sem->count */
69 "jle 2f\n"
70 "1:\n"
71 ".section .text.lock,\"ax\"\n"
72 "2:\tcall __up_wakeup\n\t"
73 "jmp 1b\n"
74 ".previous"
75 :"=m" (sem->count)
76 :"c" (sem)
77 :"memory");
}

(arch/i386/kernel/semaphore.c):
asm(
".text\n"
".align 4\n"
".globl __up_wakeup\n"
"__up_wakeup:\n\t"
78 "pushl 陎\n\t"
79 "pushl 韝\n\t"
80 "pushl 靫\n\t"
81 "call __up\n\t"
82 "popl 靫\n\t"
83 "popl 韝\n\t"
84 "popl 陎\n\t"
85 "ret"
);

(arch/i386/kernel/semaphore.c):
void __up(struct semaphore *sem)
{
86 wake_up(&sem->wait);
}

场景七:
count值为0,有一个进程释放了该信号量。
程式会在第68行对count进行加1操作,第69行判断发现count值大于0,所以在第70行直接返回。

场景八:
count值为-1,有一个进程释放了该信号量,有一个进程在等待队列中睡眠。
进程A会在第68行对count进行加1操作,结果为0,第69行判断发现count值小于等于0,所以跳至第72行执行__up_wakeup,进而在 第81行执行__up函数,最后在第86行执行wake_up函数,唤醒在等待队列队列头的进程B。进程B被唤醒后,将继续执行第43,44行,继而执行 到第35行,在这里,sleepers的值为1,经过减1[第35行]操作后,将这个值加回到count中[第35行],结果count仍为0,所以执行 第36行,将sleepers的值设为0,表示状态为唤醒进程的过程中,然后跳出循环,做一些解琐和清除队列的操作后,调用wake_up函数[第49 行],由于这个场景中只有一个进程在等待队列中睡眠,所以这里的wake_up函数将什么也不做。这时的状态为count为0,sleepers为0,没 有其他进程在等待队列中睡眠。进程B最终会调用一个up函数,而进入场景七。

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!