今天我们详细了解一下spin_lock在内核中代码实现,我们总共分析四个项目:

1.spinlock_t的定义分析:

首先来看一下spinlock_t的定义:

typedef struct {

raw_spinlock_t raw_lock;

#if defined(CONFIG_PREEMPT) &&defined(CONFIG_SMP)

unsigned int break_lock;

#endif

#ifdef CONFIG_DEBUG_SPINLOCK

unsigned int magic, owner_cpu;

void *owner;

#endif

} spinlock_t;

从上面代码来分析一个完整的spinlock_t的结构有5个成员:raw_lock/ break_lock/ magic/ owner_cpu/ owner,但是这5个成员都没有初始值,所以显然要一个函数去初始化它们。

2. spin_lock_init函数分析

我们通常用spinlock_tlock来定义一把自旋锁,然后要初始化自旋锁通过函数spin_lock_init(&lock);这个函数的定义为

/**********************************************************************/

#define spin_lock_init(lock)   do { *(lock) = SPIN_LOCK_UNLOCKED; } while(0)

/**********************************************************************/

从代码分析,所谓初始化就是把一个unlock宏的值赋给lock,从字面的意思来看就是把锁初始化为解锁,那么我们再追踪这个宏的定义:

# defineSPIN_LOCK_UNLOCKED                                        /

(spinlock_t)    {     .raw_lock= __RAW_SPIN_LOCK_UNLOCKED,       /

.magic= SPINLOCK_MAGIC,            /

.owner= SPINLOCK_OWNER_INIT,        /

.owner_cpu= -1 }

这样就很清晰了,初始化的目的就是把spinlock_t中的各个成员赋了初始值。

第一个成员raw_lock被赋予了__RAW_SPIN_LOCK_UNLOCKED是什么意思呢,继续追踪,这个宏在include/linuxspinlock_types_up.h中定义的为:

#define __RAW_SPIN_LOCK_UNLOCKED { 1 } 所以锁初始化时是把成员raw_lock赋值为1,即解锁状态。其他的初始值的含义我尚不了解

3. 加锁宏spin_lock(lock)宏的分析:

宏的定义如下:

#define spin_lock(lock)                _spin_lock(lock)

继续追踪其中的函数_spin_lock(lock)定义如下:

void __lockfunc _spin_lock(spinlock_t*lock)

{

preempt_disable();

_raw_spin_lock(lock);

}

这个函数核心就是_raw_spin_lock函数,柯南继续追踪:

void _raw_spin_lock(spinlock_t *lock)

{

debug_spin_lock_before(lock);

if(unlikely(!__raw_spin_trylock(&lock->raw_lock)))

__spin_lock_debug(lock);

debug_spin_lock_after(lock);

}

Debug的不管,那么核心函数就是__spin_lock_debug(lock),快去看看它的定义吧:

static void __spin_lock_debug(spinlock_t*lock)

{

intprint_once = 1;

u64i;

for(;;) {

for(i = 0; i < loops_per_jiffy * HZ; i++) {

cpu_relax();//空函数,不知道用意是什么

if(__raw_spin_trylock(&lock->raw_lock))

return;

}

/*lockup suspected: */

if(print_once) {

print_once= 0;

printk("BUG:spinlock lockup on CPU#%d, %s/%d, %p/n",

smp_processor_id(),current->comm, current->pid,

lock);

dump_stack();

}

}

}

哈哈,看到for (;;)就知道死循环了,那么自旋锁很明显时不会让出CPU的,除非它能够加锁成功,否则就一直自旋吧!其中HZ是CPU相关的,一个CPU时钟每秒中跳动HZ次,这个值就是一个jiffes值。对于ARM来说1秒跳动100次,HZ为100,对于X86/PPC: 1000。loops_per_jiffy= (1<<12),转化十进制为4096。两个相乘的含义就是每个jiffes中进行4096次循环,而一秒钟里面用HZ个jiffes,两个值相乘得到的就是1秒钟进行循环的次数。从这个for循环来看,就是在1秒内不断循环进行__raw_spin_trylock动作(),如果成功就跳出死循环,如果不成功就继续循环了,这样就完成了自旋的功能,而且进程不会睡眠。

static inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
    unsigned long tmp;

__asm__ __volatile__(
"    ldrex    %0, [%1]\n"
"    teq    %0, #0\n"
"    strexeq    %0, %2, [%1]"
    : "=&r" (tmp)
    : "r" (&lock->lock), "r" (1)
    : "cc");

if (tmp == 0) {
        smp_mb();
        return 1;
    } else {
        return 0;
    }
}

ARM指令LDREX和STREX可以保证在两条指令之间进行的取值-测试操作的原子性,假设有进程A和B都试图调用上述函数获得写入锁,那么谁先执行LDREX指令谁将先获得锁。 首先这段汇编是AT&T内联汇编,首先%0代表=&r" (tmp),%1代表 "r" (&lock->lock),%2代表 "r" (1),以此类推,这样的话首先从lock中把值取出来,放到tmp里面,然后用测试指令比较tmp是否为0,如果是0代表代表没有人获得锁。然后使用strex指令把lock的值设为1,获取锁。如果lock的值不为0,表明之前该锁已经被被其他的进程所使用,那么该进程将自旋。

4. 加锁宏spin_lock(lock)宏的分析:

#define spin_unlock(lock)            _spin_unlock(lock)

无需多言

void __lockfunc _spin_unlock(spinlock_t*lock)

{

_raw_spin_unlock(lock);

preempt_enable();

}

这个函数中先调用_raw_spin_unlock,然后preempt_enable(什么含义,fixme)。

void _raw_spin_unlock(spinlock_t *lock)

{

debug_spin_unlock(lock);

__raw_spin_unlock(&lock->raw_lock);

}

继续追踪__raw_spin_unlock(&lock->raw_lock)内核真的很能绕弯子,大家慢慢习惯了

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
    smp_mb();

__asm__ __volatile__(
"    str    %1, [%0]"
    :
    : "r" (&lock->lock), "r" (0)
    : "cc");
}

不就是给lock成员赋值0嘛,呵呵,解锁完成。

至此完成自旋锁的初步分析,基本脉络清楚了,代码中还有部分不了解含义,以后再研究,到此休息一下。

spin_lock浅析相关推荐

  1. 计算机科学精彩帖子收集

    linux源码 LXR 源自"the Linux Cross Referencer",中间的"X"形象地代表了"Cross".与 Sourc ...

  2. linux进程调度浅析

    linux进程调度浅析 操作系统要实现多进程,进程调度必不可少. 有人说,进程调度是操作系统中最为重要的一个部分.我觉得这种说法说得太绝对了一点,就像很多人动辄就说"某某函数比某某函数效率高 ...

  3. 【Linux系统编程】Linux 进程调度浅析

    概述 操作系统要实现多进程,进程调度必不可少.有人说,进程调度是操作系统中最为重要的一个部分.我觉得这种说法说得太绝对了一点,就像很多人动辄就说"某某函数比某某函数效率高XX倍"一 ...

  4. linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

  5. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  6. 浅析Python中bytes和str区别

    本博转载自:Chown-Jane-Y的浅析Python3中的bytes和str类型 Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示, ...

  7. 学习《Linux设备模型浅析之设备篇》笔记(深挖一)

    这篇文章既然说了是浅析,那就是跳过了一些东西,我们把这些跳过的东西给它尽可能的补回来 今天登陆 lxr.free-electrons.com 发现内核版本已经升级到3.15了,那以后都使用3.15的源 ...

  8. 学习《Linux设备模型浅析之设备篇》笔记(三)

    文件/drivers/base/core.c static struct kobject *get_device_parent(struct device *dev, struct device *p ...

  9. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

最新文章

  1. android 解决Error:This Gradle plugin requires Studio 3.0 minimum
  2. spring roo_使用Spring Roo进行快速云开发–第1部分:Google App Engine(GAE)
  3. 第3章   IP寻址
  4. 《架构即未来》中最常用的15个架构原则
  5. php 支持泛型,PHP对Java样式类的泛型有答案吗?
  6. GBase 8a开启防火墙安装集群添加端口策略
  7. 开发撞墙之奇怪的需求:经纬度带符号转换
  8. Windows蓝屏为什么是蓝底白字?微软程序员揭开了秘密
  9. python battleship_Python 入门教程 12 ---- Battleship!-阿里云开发者社区
  10. android手机是否root,已经2017年了,安卓手机还需要Root吗?
  11. 程序员如何获取招聘信息
  12. Mysql期初数和期末数_账户中记录四种核算指标,即期初余额、 本期增加发生额、本期减少发生额和期末余额。其关系式包括( )。_学小易找答案...
  13. Matlab基础学习(中)
  14. switch 语句的参数类型
  15. 苹果笔记本恢复服务器上的安装信息已被破坏,Mac电脑如何恢复删除或已损坏无法打开的Word文件?...
  16. 创建 Swift 自定义集合类
  17. 小米门卡协议加载失败_小米门禁卡没反应怎么回事
  18. n维椭球体积公式_2020中考物理知识汇总:公式大全
  19. 【python拼图】遍历文件夹后,自动拼接图像成正方形图,或者指定行数显示
  20. VScode怎么开启本地服务器及本地调试?

热门文章

  1. 王权理财:鹰派寒气逼人金价开启跌势,天然气跌势暂缓有望反弹拉升
  2. 毕业答辩ppt计算机科学与技术,计算机科学与技术专业论文答辩PPT范例.ppt
  3. No serializer found for class com.xxx and no properties discovered to create BeanSerializer
  4. 第五次网页前端培训(JavaScript)
  5. SQL SERVER中SQL格式化插件——SQL Pretty Printer
  6. 神经网络学习9--过拟合 (Overfitting)的解决办法
  7. Android对ScrollView滚动监听,实现美团、大众点评的购买悬浮效果
  8. Web自动化测试-Protractor基础(一)
  9. Java动态代理原理分析
  10. CanOpen and EtherCAT