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

2016-01-29 18:12 11 1 收藏

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

【 tulaoshi.com - Linux 】

信号量作为一种同步机制,在每个成熟的现代操作系统的实现过程中,起着不可替代的作用,对于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 operationnt"3 LOCK "decl %0nt" /* --sem->count */4 "js 2fn"5 "1:n"6 ".section .text.lock,"ax"n"7 "2:tcall __down_failednt"8 "jmp 1bn"9 ".previous"10 :"=m" (sem->count)11 :"c" (sem)12 :"memory");}__down_fail()调用__down(),(arch/i386/kernel/semaphore.c):asm(".textn"".align 4n"".globl __down_failedn""__down_failed:nt"13 "pushl %eaxnt"14 "pushl %edxnt"15 "pushl %ecxnt"16 "call __downnt"17 "popl %ecxnt"18 "popl %edxnt"19 "popl %eaxnt"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

来源:http://www.tulaoshi.com/n/20160129/1504810.html

延伸阅读
    所有的Unix内核都同宗同源,并且提供相同的API,现代的Unix内核存在许多设计上的相似之处。Unix内核几乎毫无例外的都是一个不可分割的静态可执行块(文件)。也就是说,它们必须以完整、单独的可执行块的形式在一个单独的地址空间中运行。Unix内核几乎都需要硬件系统提供页机制以管理内存。这种页机制可以加强内存空间的保...
随着Linux2.6的发布,由于2.6内核做了教的改动,各个设备的驱动程序在不同程度上要进行改写。为了方便各位Linux爱好者我把自己整理的这分文档share出来。该文当列举了2.6内核同以前版本的绝大多数变化,可惜的是由于时间和精力有限没有详细列出各个函数的用法。 特别声明:该文档中的内容来自http://lwn.net,该网也上也有各个函数的较为详细...
  针对好多Linux 爱好者对内核很有兴趣却无从下口,本文旨在介绍一种解读linux内核源码的入门方法, 而不是解说linux复杂的内核机制; 一.核心源程序的文件组织: 1.Linux核心源程序通常都安装在/usr/src/linux下,而且它有一个非常简单的编号约定:任何偶数的 核心(例如2.0.30)都是一个稳定地发行的核心,而任何奇数的核心(...
  第十四章 Linux核心资源 本章主要描叙寻找某个特殊核心函数时用到的Linux核心资源。 本书并不要求读者具有C编程语言的能力或者拥有Linux核心源代码来理解Linux核心工作原理。但是如果 对核心源代码进行阅读将加深对Linux操作系统的理解。本章提供了一个核心源代码的综述。 从哪里得到Linux核心源码 所有主要Linux分发版本...
Linux中的大部分驱动程序,是以模块的形式编写的.这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载. 一个典型的驱动程序,大体上可以分为这么几个部分: 1,注册设备 在系统初启,或者模块加载时候,必须将设备登记到相应的设备数组,并返回设备的主驱动号,例如:对快设备来说调...

经验教程

143

收藏

30
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部