转自:http://blog.csdn.net/frankyzhangc/article/details/6569475

版权声明:本文为博主原创文章,未经博主允许不得转载。

今天我们详细了解一下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 设备模型浅析之 uevent 篇(2)

    Linux 设备模型浅析之 uevent 篇 本文属本人原创,欢迎转载,转载请注明出处.由于个人的见识和能力有限,不可能面 面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是 yzq.seen@gma ...

  2. Linux系统编程——进程调度浅析

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

  3. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程(转自http://blog.chinaunix.net/uid-20564848-id-73480.html)

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

  4. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】

    转自:http://blog.chinaunix.net/uid-20564848-id-73480.html 浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_time ...

  5. SQL Server on Linux 理由浅析

    SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...

  6. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  7. 高性能IO模型浅析

    高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking  ...

  8. netty5 HTTP协议栈浅析与实践

      一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...

  9. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

随机推荐

  1. Ubuntu18.04 + CUDA9.0 + cuDNN7.3 + Tensorflow-gpu-1.12 + Jupyter Notebook深度学习环境配置

    目录 一.Ubuntu18.04 LTS系统的安装 1. 安装文件下载 2. 制作U盘安装镜像文件 3. 开始安装 二.设置软件源的国内镜像 1. 设置方法 2.关于ubuntu镜像的小知识 三.Nv ...

  2. annoy安装

    yum install gcc-c++ #linux下需安装c++编译器 sudo pip install annoy

  3. kaldi常用文件查看指令

    目录 1. ark特征文件 2. FST文件 3. mdl模型文件 4. 决策树文件 5. ali.gz对齐文件 资料来自kaldi官方文档. 转载注明出处. 1. ark特征文件 copy-feat ...

  4. kickstart技术安装操作系统

    kickstart是RedHat公司开源的软件,所以对CentOS兼容性最好. 原理:我们将手动安装的所有的详细步骤记录到一个文件中,然后kickstart通过读取这个文件就可以实现自动化安装系统. ...

  5. Mapper的方式总结

    Mapper的方式总结: <mappers> <!-- 通过package元素将会把指定包下面的所有Mapper接口进行注册 --> <package name=&quo ...

  6. HDU 4569 Special equations(枚举+数论)(2013 ACM-ICPC长沙赛区全国邀请赛)

    Problem Description Let f(x) = anxn +...+ a1x +a0, in which ai (0 <= i <= n) are all known int ...

  7. jqprint导入jqgrid表格时,内容溢出的原因以及解决方法

    jqprint在导入表格的时候,会将原表格的样式全部拉过来,所以说原表格(如jqgrid的表格)的内容在有滚动条的时候,必须得将宽度设置为100%(等百分比的宽度),不能设置成固定宽度,不然表格内容会 ...

  8. 第十四次ScrumMeeting会议

    第十三次Scrum Meeting 时间:2017/12/2 地点:咖啡馆 人员:策划组美工组 目前工作情况 名字 完成的工作 计划工作 蔡帜 科技树策划设计,科技图鉴蓝图设计 员工方面细节设定 游心 ...

  9. beta版本冲刺七

    目录 组员情况 组员1:胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:恺琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:何宇恒 组员11:刘一好 展示组内最新 ...

  10. 指针C语言

    一.PTA实验作业 题目一:6-7输出月份英文名 1.PTA提交列表 2.设计思路和流程图 这题只需补充子函数,定义指针数组month[12],分别从一月到十二月,再定义一个字符,让它为NULL,当输 ...