Chinaunix首页 | 论坛 | 博客
  • 博客访问: 473619
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1693
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-13 09:23
个人简介

前EMC高级软件工程师,现小米分布式存储码农,关注分布式存储,文件系统,Linux内核。微博: http://weibo.com/u/2203007022

文章分类

全部博文(17)

文章存档

2015年(1)

2014年(6)

2013年(10)

分类: LINUX

2013-09-21 20:26:11

      传统的自旋锁实际上就是一个整数,值为1时表示没有被占用,值为0或负数时表示锁已经被占用,此时spin_lock循环等待,直到spin_unlock将自旋锁的值置为1,在这个过程中没有保存线程申请自旋锁的顺序信息,后进入等待的线程有可能先获得自旋锁。
      排队自旋锁(FIFO Ticket Spinlock)是 Linux 内核 2.6.25 版本引入的一种新型自旋锁,它通过保存执行线程申请锁的顺序信息解决了这种问题,让先申请自旋锁的线程先得到锁。
1. Ticket spinlock的实现原理
        排队自旋锁还是使用一个整形slock,并将其分为两个部分:
                   Ticket Number 
        Next和Owner的长度与CPU的个数相关, 当CPU的个数 < 256时,Next和Owner为8位。当CPU的个数 > 256时,Next和Owner为16位。

#if (NR_CPUS < 256)

#define TICKET_SHIFT 8

...
#else
#define TICKET_SHIFT 16

        slock初始化时被置为0,Next和Owner都被置为0,当内核线程申请自旋锁时,比较原始的Next和Owner并将Next加1,如果原始的Next域和Owner域相等则表示锁处理未使用状态,否则改线程轮询等待直到Next域和Owner域相等。当释放锁时将Owner域加1.

2. Ticket spinlock的实现代码
下面查看__ticket_spin_lock和__ticket_spin_unlock两个函数(NR_CPUS < 256):
加锁:

点击(此处)折叠或打开

  1. static __always_inline void __ticket_spin_lock(arch_spinlock_t *lock)
  2. {
  3.   short inc = 0x0100;

  4.   asm volatile (
  5.     LOCK_PREFIX "xaddw %w0, %1\n"
  6.     "1:\t"
  7.     "cmpb %h0, %b0\n\t"
  8.     "je 2f\n\t"
  9.     "rep ; nop\n\t"
  10.     "movb %1, %b0\n\t"
  11.     /* don't need lfence here, because loads are in-order */
  12.     "jmp 1b\n"
  13.     "2:"
  14.     : "+Q" (inc), "+m" (lock->slock)
  15.     :
  16.     : "memory", "cc");
  17. }

a. xaddw %w0, %1: 将inc和lock->slock的低16位置交换,并将相加后的值存贷lock->slock中. 
        例:slock = 0x00 00 11 10,操作之后slock = 0x00 00 12 10, inc = 0x00 00 11 10.
b. cmpb %h0, %b0: 比较inc的低8位(Owner)和高8位(Next),相等则获得锁返回,不相等则继续执行;
c. 不断轮询lock->slock, 等待直到Next和Owner相等。

解锁

点击(此处)折叠或打开

  1. static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)
  2. {
  3.   asm volatile(UNLOCK_LOCK_PREFIX "incb %0" // 将owner加1
  4.          : "+m" (lock->slock)
  5.          :
  6.          : "memory", "cc");
  7. }
a. 解锁只执行一个操作就是讲slock的Owner字段加1;

通过这种方式,线程调用__ticket_spin_lock的顺序存放在Next字段中,Next字段小的线程会先得到锁。


 


阅读(5586) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~