文章目录

  • 一、代码阅读分析
    • 0、spin lock调用流程图
    • 1、再kernel中调用spi_lock()或spin_unlock函数
    • 2、调用raw_spin_lock()和raw_spin_unlock()
    • 3、调用_raw_spin_lock()和_raw_spin_unlock()
    • 4、调用__raw_spin_lock()和__raw_spin_unlock()
    • 5、调用do_raw_spin_lock()和do_raw_spin_unlock()
    • 6、调用arch_spin_lock()和arch_spin_unlock()
    • 7、在arm64中arch_spin_lock()和arch_spin_unlock()的实现
    • 8、在arm32中arch_spin_lock()和arch_spin_unlock()的实现
    • 8、相关结构体
  • 二、spin lock的排队原理
  • 九、spin lock的总结

一、代码阅读分析

0、spin lock调用流程图

1、再kernel中调用spi_lock()或spin_unlock函数

spin_lock(&aacirun->lock)
spin_unlock(&aacirun->lock)

2、调用raw_spin_lock()和raw_spin_unlock()

linux/include/linux/spinlock.hstatic __always_inline void spin_lock(spinlock_t *lock)
{raw_spin_lock(&lock->rlock);
}static __always_inline void spin_lock_bh(spinlock_t *lock)
{raw_spin_lock_bh(&lock->rlock);
}
static __always_inline void spin_lock_irq(spinlock_t *lock)
{raw_spin_lock_irq(&lock->rlock);
}#define spin_lock_irqsave(lock, flags)             \
do {                                \raw_spin_lock_irqsave(spinlock_check(lock), flags);    \
} static __always_inline void spin_unlock(spinlock_t *lock)
{raw_spin_unlock(&lock->rlock);
}static __always_inline void spin_unlock_bh(spinlock_t *lock)
{raw_spin_unlock_bh(&lock->rlock);
}static __always_inline void spin_unlock_irq(spinlock_t *lock)
{raw_spin_unlock_irq(&lock->rlock);
}static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{raw_spin_unlock_irqrestore(&lock->rlock, flags);
}

3、调用_raw_spin_lock()和_raw_spin_unlock()

#define raw_spin_lock(lock) _raw_spin_lock(lock)
#define raw_spin_unlock(lock) _raw_spin_unlock(lock)

4、调用__raw_spin_lock()和__raw_spin_unlock()

(linux/include/linux/spinlock_api_smp.h)#ifdef CONFIG_INLINE_SPIN_LOCK
#define _raw_spin_lock(lock) __raw_spin_lock(lock)
#endif(linux/kernel/locking/spinlock.c)
#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif

5、调用do_raw_spin_lock()和do_raw_spin_unlock()

(linux/include/linux/spinlock_api_smp.h)
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{preempt_disable();spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{spin_release(&lock->dep_map, 1, _RET_IP_);do_raw_spin_unlock(lock);preempt_enable();
}

在这一层中,我们看到了preempt_disable()和preempt_enable(),禁止抢占和允许抢占.

6、调用arch_spin_lock()和arch_spin_unlock()

(linux/include/linux/spinlock.h)
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{__acquire(lock);arch_spin_lock(&lock->raw_lock);
}static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
{arch_spin_unlock(&lock->raw_lock);__release(lock);
}

7、在arm64中arch_spin_lock()和arch_spin_unlock()的实现

在arch_spin_unlock中为什么没有sev指令,lock中的wfe在什么地方被唤醒呢?
等待自旋锁的时候,使用指令ldaxrh(带有获取语义的独占加载,h表示halfword,即2字节)读取服务号,独占加载操作会设置处理器的独占监视器,记录锁的物理地址。
释放锁的时候,使用stlrh指令修改锁的值,stlrh指令会清除所有监视锁的物理地址的处理器的独占监视器,清除独占监视器的时候会生成一个唤醒事件。

(linux/arch/arm64/asm/spinlock.h)
static inline void arch_spin_lock(arch_spinlock_t *lock)
{unsigned int tmp;arch_spinlock_t lockval, newval;asm volatile(/* Atomically increment the next ticket. */ARM64_LSE_ATOMIC_INSN(/* LL/SC */
"  prfm    pstl1strm, %3\n"
"1:    ldaxr   %w0, %3\n"
"  add %w1, %w0, %w5\n"
"  stxr    %w2, %w1, %3\n"
"  cbnz    %w2, 1b\n",/* LSE atomics */
"  mov %w2, %w5\n"
"  ldadda  %w2, %w0, %3\n"
"  nop\n"
"  nop\n"
"  nop\n")/* Did we get the lock? */
"  eor %w1, %w0, %w0, ror #16\n"
"  cbz %w1, 3f\n"/** No: spin on the owner. Send a local event to avoid missing an* unlock before the exclusive load.*/
"  sevl\n"
"2:    wfe\n"
"  ldaxrh  %w2, %4\n"
"  eor %w1, %w2, %w0, lsr #16\n"
"  cbnz    %w1, 2b\n"/* We got the lock. Critical section starts here. */
"3:": "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock): "Q" (lock->owner), "I" (1 << TICKET_SHIFT): "memory");
}static inline void arch_spin_unlock(arch_spinlock_t *lock)
{unsigned long tmp;asm volatile(ARM64_LSE_ATOMIC_INSN(/* LL/SC */" ldrh    %w1, %0\n""   add %w1, %w1, #1\n""  stlrh   %w1, %0",/* LSE atomics */"   mov %w1, #1\n""   nop\n""   staddlh %w1, %0"): "=Q" (lock->owner), "=&r" (tmp):: "memory");
}

8、在arm32中arch_spin_lock()和arch_spin_unlock()的实现

(linux/arch/arm/asm/spinlock.h)
static inline void arch_spin_lock(arch_spinlock_t *lock)
{unsigned long tmp;u32 newval;arch_spinlock_t lockval;prefetchw(&lock->slock);__asm__ __volatile__(
"1:    ldrex   %0, [%3]\n"
"  add %1, %0, %4\n"
"  strex   %2, %1, [%3]\n"
"  teq %2, #0\n"
"  bne 1b": "=&r" (lockval), "=&r" (newval), "=&r" (tmp): "r" (&lock->slock), "I" (1 << TICKET_SHIFT): "cc");while (lockval.tickets.next != lockval.tickets.owner) {wfe();lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);}smp_mb();
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{smp_mb();lock->tickets.owner++;dsb_sev();
}

8、相关结构体

(1)、spinlock_t
spinlock_t 结构体中,只有一个struct raw_spinlock rlock元素

typedef struct spinlock {union {struct raw_spinlock rlock;#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))struct {u8 __padding[LOCK_PADSIZE];struct lockdep_map dep_map;};
#endif};
} spinlock_t;

(2)、raw_spinlock
在raw_spinlock中,有arch_spinlock_t raw_lock

typedef struct raw_spinlock {arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAKunsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCKunsigned int magic, owner_cpu;void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;
#endif
} raw_spinlock_t;

(3)、arch_spinlock_t

typedef struct {#ifdef __AARCH64EB__u16 next;u16 owner;
#elseu16 owner;u16 next;
#endif
} __aligned(4) arch_spinlock_t;

二、spin lock的排队原理

我们将arch_spinlock_t单独拎出来、将处理ower和next的地方也单独拎出来,翻译成C语言是这一个样子的。
这种做法的目的,主要是引入排队机制,谁先申请,谁先获得

struct spinlock {unsigned short owner;unsigned short next;
};void spin_lock(struct spinlock *lock)
{unsigned short next = xadd(&lock->next, 1);while (lock->owner != next);
}void spin_unlock(struct spinlock *lock)
{lock->owner++;
}

我们举个例子:

         init  cpu0-acquire  cpu4-acquire  cpu6-acquire  cpu1-acquire
owner    1        1               1             1            1
next     0        1               2             3            4
  • 在spin_lock_init时,owenr=1, next=0;
  • 当cpu0 acquire锁时,next++后,next=1,在spin_lock中while循环成立,程序继续往下跑;
  • 此时,cpu4也试图拿锁,next++后,next=2,程序卡在while循环中;
  • 此时,cpu6也试图拿锁,next++后,next=3,程序卡在while循环中;
  • 此时,cpu1也试图拿锁,next++后,next=4,程序卡在while循环中;
  • 等到cpu0释放该锁了,owner++,owner=2,此时cpu4中的while循环退出,程序继续往下跑;

注意:在spin_lock_init时,初始化owenr=1, next=0

void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,struct lock_class_key *key)
{#ifdef CONFIG_DEBUG_LOCK_ALLOC/** Make sure we are not reinitializing a held lock:*/debug_check_no_locks_freed((void *)lock, sizeof(*lock));lockdep_init_map(&lock->dep_map, name, key, 0);
#endiflock->raw_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;lock->magic = SPINLOCK_MAGIC;lock->owner = SPINLOCK_OWNER_INIT;lock->owner_cpu = -1;
}#define __ARCH_SPIN_LOCK_UNLOCKED { 1 }

九、spin lock的总结

spi lock都干了那些事:
在spin_lock()时:

  • 禁止抢占;
  • 引入onwer/next排队机制循环执行while(1)排队;
  • 为了降低功耗引入wfe/sev指令,未获取该锁的cpu进程就是低功耗状态,等到有人释放该锁了再去执行while(1)排队

linux kernel的spinlock代码导读和分析相关推荐

  1. Linux Kernel 2.6.9源码分析 -- send/recieve 报文

    Linux Kernel 2.6.9源码分析 – send/recieve 报文 可用户socket报文读写的函数有以下几对: ssize_t read(int fd, void *buf, size ...

  2. Linux内核学习(五):linux kernel源码结构以及makefile分析

    Linux内核学习(五):linux kernel源码结构以及makefile分析 前面我们知道了linux内核镜像的生成.加载以及加载工具uboot. 这里我们来看看linux内核的源码的宏观东西, ...

  3. CVE-2020-8835: Linux Kernel 信息泄漏/权限提升漏洞分析

    CVE-2020-8835: Linux Kernel 信息泄漏/权限提升漏洞分析 360-CERT [360CERT](javascript:void(0)

  4. Linux kernel 3.10内核源码分析--进程上下文切换

    一.疑问 进程调度时,当被选中的next进程不是current进程时,需要进行上下文切换. 进行上下文切换时,有一些问题不太容易理解,比如: 1.进程上下文切换必然发生在内核态吗? 2.上下文切换后原 ...

  5. Linux kernel 3.10内核源码分析--TLB相关--TLB概念、flush、TLB lazy模式

    一.概念及基本原理 TLB即Translation Lookaside Buffer,是MMU中的一种硬件cache,用于缓存页表,即缓存线性地址(虚拟地址)到物理地址的映射关系. 如果没有TLB,那 ...

  6. Linux kernel 3.10内核源码分析--slab原理及相关代码

    1.基本原理 我们知道,Linux保护模式下,采用分页机制,内核中物理内存使用buddy system(伙伴系统)进行管理,管理的内存单元大小为一页,也就是说使用buddy system分配内存最少需 ...

  7. linux kernel的spinlock在armv7和armv8中的不同

    在armv7中:spin_lock调用了wfe指令,让cpu进入低功耗状态;在spin_unlock中调用了sev指令,让cpu退出低功耗模式; 在armv8中,spin_lock调用了wfe指令,让 ...

  8. Linux Kernel 3.10内核源码分析--块设备层request plug/unplug机制

    一.基本原理 Linux块设备层使用了plug/unplug(蓄流/泄流)的机制来提升IO吞吐量.基本原理为:当IO请求提交时,不知直接提交给底层驱动,而是先将其放入一个队列中(相当于水池),待一定时 ...

  9. LK(little kernel)第一行代码链接位置分析及lk启动过程

    LK是(L)ittle (K)ernel的缩写,是一个功能及其强大的bootloader开源项目,但现在只支持arm和x86平台. LK的一个显著的特点就是它实现了一个简单的线程机制(thread), ...

最新文章

  1. IOS开发 使用CGContextRef绘制文字时的设置
  2. 【鸿蒙 HarmonyOS】UI 组件 ( 拖动条 Slider 组件 )
  3. C++ Primer 5th笔记(chap 17 标准库特殊设施)正则表达式错误
  4. 学会用各种方式备份MySQL数据库
  5. wxWidgets:WxBase 事件循环
  6. Spark-三大数据结构之-广播变量
  7. SSM项目调用Dao层查询方法传入正确参数但查不到数据
  8. 程序员的算法课(20)-常用的图算法:最小生成树(MST)
  9. 记一道简单的Java面试题,但答错率很高!
  10. bitnami mysql_最新 Bitnami redmine安装与配置
  11. 1 企业实战(3) Redis服务部署和配置详解 (资源)
  12. 我们该不该在Rust上做点投资?
  13. centos6.5系统执行mv /* /path,只能用cd命令,如何恢复
  14. 找到小菇凉 (BFS)
  15. cropper(裁剪图片)插件使用(案例)
  16. freeswitch软电话配置、结合讯时网关,外线电话呼入、呼出配置
  17. 90后凤凰男:寒门难出贵子
  18. Go语言编程笔记16:存储数据
  19. 测试不同体重体型软件样子的,hikaku-sitatter身高软件,一键测试自己的体型
  20. css vue 内联_vue 内联样式style中的background

热门文章

  1. 关于精密空调,你需要了解的都在这里!
  2. 精密空调机组及零部件相关专业术语
  3. 成功解决ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or
  4. 成功解决启动SQLServer失败,根据错误信息判断错误故障
  5. AI:2020年6月22日北京智源大会演讲分享之机器感知专题论坛—14:10-14:50王亮教授《面向复杂视觉任务的视觉认知计算》
  6. DL之MobileNetV2:MobileNetV2算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  7. BC之链式块状结构:区块链之链式块状结构——链式块状结构、内容相关配图
  8. ##API(七)————日期操作类(二)
  9. redis 数据类型、命令
  10. 【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆优化Dijkstra