当前位置: 首页 > 图文教程 > 操作系统 > Unix/Linux > 详谈Linux 2_4_x内核信号量机制

Unix/Linux
如何在linux下面运行招商银行专业版
Windows和Linux系统共存 完美解决方案
Linux 和对称多处理应用程序介绍
Red Hat Linux 附加引导和内核引导选项
Linux 2.6.11内核文件IO系统调用详解
走向高手之路 Linux系统内核技术解读
菜鸟乐园 Linux系统内核代码特色一览
Linux内核和传统Unix内核的比较
内核中的物理内存分配函数kernel api
使用 Linux 系统调用的内核命令
详细解析 Linux内核的主要配置选项
内核操作 Linux2.6内核驱动移植参考
Linux内存中Swap和Buffer Cache机制
高手进阶 更换Fedora Core 6的内核
高手风范 Linux操作系统内核编码风格
Linux操作系统内核中工作队列的操作
揭开Linux系统内核调试器的神秘面纱
系统“大挪移” Linux服务器迁移大法
高手进阶 重新编译Linux操作系统的内核
Ubuntu系统关于驱动模块命令的一些使用

Unix/Linux 中的 详谈Linux 2_4_x内核信号量机制


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-11-01   浏览: 260 ::
收藏到网摘: n/a

信号量作为一种同步机制,在每个成熟的现代操作系统的实现过程中,起着不可替代的作用,对于Linux也不例外,在Linux2_4_x下,实现内核信号量机制的代码虽然不长,但由于涉及到多个进程间的相互干扰,并且在Linux发展过程中,不断进行优化,所以非常难于理解,在讲解Linux源代码的各类文章中,也大都对此语焉不详,本人通过认真阅读,对这部分代码有了一定的了解,这里介绍出来,希望对大家有所帮助。
信号量作为一种同步机制,在每个成熟的现代操作系统的实现过程中,起着不可替代的作用,对于Linux也不例外,在Linux2_4_x下,实现内核信号量机制的代码虽然不长,但由于涉及到多个进程间的相互干扰,并且在Linux发展过程中,不断进行优化,所以非常难于理解,在讲解Linux源代码的各类文章中,也大都对此语焉不详,本人通过认真阅读,对这部分代码有了一定的了解,这里介绍出来,希望对大家有所帮助。 首先看看信号量的概念: 1.信号量的类型定义: 每个信号量至少须记录两个信息:信号量的值和等待该信号量的进程队列。它的类型定义如下:(用类PASCAL语言表述) semaphore = record value: integer; queue: ^PCB; end; 其中PCB是进程控制块,是操作系统为每个进程建立的数据结构。s.value>=0时,s.queue为空; s.value<0时,s.value的绝对值为s.queue中等待进程的个数; 2.PV原语: 对一个信号量变量可以进行两种原语操作:p操作和v操作,定义如下: procedure p(var s:samephore); { s.value=s.value-1; if (s.value<0) asleep(s.queue); } procedure v(var s:samephore); { s.value=s.value+1; if (s.value<=0) wakeup(s.queue); } 其中用到两个标准过程: asleep(s.queue);执行此操作的进程的PCB进入s.queue尾部,进程变成等待状态wakeup(s.queue);将s.queue头进程唤醒插入就绪队列。s.value初值为1时,可以用来实现进程的互斥。p操作和v操作是不可中断的程序段,称为原语。如果将信号量看作共享变量,则pv操作为其临界区,多个进程不能同时执行,一般用硬件方法保证。一个信号量只能置一次初值,以后只能对之进行p操作或v操作。 再来看看在对应的Linux2.4.x具体实现中,信号量的数据结构: struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; };可以看到相对于操作系统原理中的定义,数据结构多了一个sleepers成员,这个变量是为使主要分支代码执行速度更快,所作的优化。 其中,count相当于资源计数,为正数或0时表示可用资源数,-1则表示没有空闲资源且有等待进程,而其他的负数仅仅一个中间过程,当所有进程都稳定下来后,将最终变为-1。 sleepers相当于一个状态标志,它仅有0和1两个值,有时会出现2,但也只是中间状态,并没有实际意义。当sleepers为0时,表示没有进程在等待,或等待中的这些进程正在被唤醒过程中。当sleepers为1时,表示至少有一个进程在等待中 wait是等待队列。 在这里可以看到,等待进程的数量并不被关心。 下面我们来看函数,Linux2.4.x具体实现中,down()函数相当于p操作,up()函数相当于v操作。在down()函数中,count做原子减1操作,如果结果不小于0[第4行],则表示成功申请,从down()中返回,如果结果为负(大多数情况是-1,注意判断“结果不小于0”的原因,是因为结果有可能是其他负数),表示需要等待,则调用__down_fail()[第7行],(include/asm-i386/semaphore.h):static inline void down(struct semaphore * sem){1 __asm__ __volatile__(2 # atomic down operation\n\t"3 LOCK "decl %0\n\t" /* --sem->count */4 "js 2f\n"5 "1:\n"6 ".section .text.lock,\"ax\"\n"7 "2:\tcall __down_failed\n\t"8 "jmp 1b\n"9 ".previous"10 :"=m" (sem->count)11 :"c" (sem)12 :"memory");}__down_fail()调用__down(),(arch/i386/kernel/semaphore.c):asm(".text\n"".align 4\n"".globl __down_failed\n""__down_failed:\n\t"13 "pushl %eax\n\t"14 "pushl %edx\n\t"15 "pushl %ecx\n\t"16 "call __down\n\t"17 "popl %ecx\n\t"18 "popl %edx\n\t"19 "popl %eax\n\t"20 "ret");__down()用C代码实现。(arch/i386/kernel/semaphore.c):void __down(struct semaphore * sem){21 struct task_struct *tsk = current;22 DECLARE_WAITQUEUE(wait, tsk);23 tsk->state = TASK_UNINTERRUPTIBLE;24 add_wait_queue_exclusive(&sem->wait, &wait);2526 spin_lock_irq(&semaphore_lock);27 sem->sleepers++;28 for (;;) {29 int sleepers = sem->sleepers;3031 /*32 * Add "everybody else" into it. They aren't33 * playing, because we own the spinlock.34 */35 if (!atomic_add_negative(sleepers - 1, &sem->count)) {36 sem->sleepers = 0;37 break;38 }39 sem->slee