Chinaunix首页 | 论坛 | 博客
  • 博客访问: 811496
  • 博文数量: 61
  • 博客积分: 958
  • 博客等级: 准尉
  • 技术积分: 2483
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-21 13:36
文章分类
文章存档

2020年(2)

2019年(1)

2018年(5)

2017年(7)

2015年(2)

2014年(4)

2012年(10)

2011年(30)

分类: LINUX

2011-11-13 19:47:06

摘要:本文主要讲述linux如何处理ARM cortex A9多核处理器的内核同步部分。主要包括读写自旋锁介绍。

法律声明LINUX3.0内核源代码分析》系列文章由谢宝友(scxby@163.com)发表于http://xiebaoyou.blog.chinaunix.net,文章中的LINUX3.0源代码遵循GPL协议。除此以外,文档中的其他内容由作者保留所有版权。谢绝转载。

 

1.1 读写自旋锁

读写自旋锁是一种特殊的自旋锁。它与自旋锁的区别:自旋锁仅仅同时允许一个读者或者写者进入临界保护区。而读写自旋锁允许多个读者同时进入临界保护区,但是不允许多个写者同时进入临界保护区,也不允许读者和写者同时进入临界保护区。

读写自旋锁的数据结构与自旋锁完全一致。但是对lock字段有了不同的解释:

/**

 * 体系结构相关的读写自旋锁。

 * ARM来说,就是一个int。其中第31位,即符号位表示是否存在写者。其余位表示读者的数量。

 */

typedef struct {

         volatile unsigned int lock;

} arch_rwlock_t;

 

1.1.1      获取和释放读锁

申请读锁的函数是read_lock,它的流程与spin_lock几乎完全一致,唯一不同的是最后调用体系结构相关的函数是arch_read_lock而不是arch_spin_lock

/**

 * 获取读自旋锁。

 */

static inline void arch_read_lock(arch_rwlock_t *rw)

{

         unsigned long tmp, tmp2;

 

         __asm__ __volatile__(

"1:    ldrex         %0, [%2]\n"/* lock字段中排它性的装载到寄存器 */

"       adds          %0, %0, #1\n"/* lock字段的值加1,并更新cpsr标志 */

"       strexpl      %1, %0, [%2]\n"/* 如果加1后是正值,表示没有写者。则排它性将值存回内存 */

         WFE("mi")

"       rsbpls       %0, %1, #0\n"/* 如果加1后是正值,%1中保存的是strexpl的结果。将指令结果取反并更新cpsr标志 */

"       bmi  1b"/* 不论是adds后,寄存器中的值变为负,还是在回写内存时不成功,此时的cpsr标志都是小于0,这两种情况都表示获取读锁不成功,继续死循环等待读锁 */

         : "=&r" (tmp), "=&r" (tmp2)

         : "r" (&rw->lock)

         : "cc");

 

         /**

          * 运行到这里,已经获得了读锁。

          * 这个屏障即可以防止读锁后面的内存操作与锁前面的操作乱序,也防止编译乱序。

          */

         smp_mb();

}

 

释放读自旋锁的过程与spin_unlock也是类似的,最终调用的体系结构相关的函数是而不是arch_spin_unlock

/**

 * 释放读自旋锁。

 */

static inline void arch_read_unlock(arch_rwlock_t *rw)

{

         unsigned long tmp, tmp2;

 

         /**

          * 这个内存屏障是为了防止锁前面的内存操作与锁内存操作乱序。使得读取的内存是错误的值。

          */

         smp_mb();

 

         __asm__ __volatile__(

"1:    ldrex         %0, [%2]\n"/* lock字段排它性装载数据到寄存器 */

"       sub   %0, %0, #1\n"/* lock字段的值减1 */

"       strex         %1, %0, [%2]\n"/* lock字段的值排它性写入内存 */

"       teq   %1, #0\n"/* 排它性写入内存的结果保存在%1中,将它与0比较 */

"       bne  1b"/* 如果%1中的值不为0,说明在写入过程中,与其他核产生了冲突,大家都试图保存lock字段的值。返回到标号1处,重试 */

         : "=&r" (tmp), "=&r" (tmp2)

         : "r" (&rw->lock)

         : "cc");

 

         if (tmp == 0)

                   dsb_sev();

}

 

1.1.2      获取和释放写锁

申请写锁的函数是write_lock,它的流程与spin_lock几乎完全一致,唯一不同的是最后调用体系结构相关的函数是arch_write_lock而不是arch_spin_lock

/**

 * 申请写自旋锁。

 */

static inline void arch_write_lock(arch_rwlock_t *rw)

{

         unsigned long tmp;

 

         __asm__ __volatile__(

"1:    ldrex         %0, [%1]\n"/* 从内存中装载lock字段的值到寄存器 */

"       teq   %0, #0\n"/* 只有当lock字段为0时,才表示没有读者和写者,此时才能成功获取写锁 */

         WFE("ne")

"       strexeq    %0, %2, [%1]\n"/* lock字段为0时,将0x80000000写入内存,0x80000000表示存在一个写者,没有读者 */

"       teq   %0, #0\n"

"       bne  1b"/* 如果排它性写入失败,或者lock字段不为0,则申请写锁失败,返回到标号1处重试 */

         : "=&r" (tmp)

         : "r" (&rw->lock), "r" (0x80000000)

         : "cc");

 

         smp_mb();

}

 

释放写锁的函数是write_unlock,它的流程与spin_unlock几乎完全一致,唯一不同的是最后调用体系结构相关的函数是arch_write_unlock而不是arch_spin_unlock

/**

 * 释放写锁

 */

static inline void arch_write_unlock(arch_rwlock_t *rw)

{

         smp_mb();

 

         __asm__ __volatile__(

         "str  %1, [%0]\n"/* 由于只会存在一个写者,因此释放写锁的过程不可能与其他锁原语冲突,因此这里不需要进行排它性装载和存储,直接向lock字段写入0即可 */

         :

         : "r" (&rw->lock), "r" (0)

         : "cc");

 

         dsb_sev();

}

 

阅读(4073) | 评论(2) | 转发(6) |
给主人留下些什么吧!~~

xiebaoyou2013-04-05 17:49:24

shoujixiaodao:请教一个问题:
     static inline void arch_read_lock(arch_rwlock_t *rw)函数解释中有“如果加1后是正值,表示没有写者”。变量lock是unsigned int 类型的。如果有写者的话也为正。还请指点。谢谢!

这里是汇编语言在处理,不受C定义的类型所影响

回复 | 举报

shoujixiaodao2013-03-29 09:47:11

请教一个问题:
     static inline void arch_read_lock(arch_rwlock_t *rw)函数解释中有“如果加1后是正值,表示没有写者”。变量lock是unsigned int 类型的。如果有写者的话也为正。还请指点。谢谢!