自旋锁

Linux的的内核最常见的锁是自旋锁。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁重新可用要是锁未被争用,请求锁的执行线程就可以立即得到它,继续执行。

在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。

同一个锁可以用在多个位置,例如,对于给定数据的所有访问都可以得到保护和同步。

-------------------------------------------------- -------------------------------------------

在Linux的2.6.11.12内核版本中,自旋锁的实现接口定义在包含\ linux的\ <spinlock.h>中,与体系结构相关的代码定义包含在\ ASM \ <spinlock.h>中。

基本结构

自旋锁的结构体是spinlock_t,

typedef struct {/*** 该字段表示自旋锁的状态,值为1表示未加锁,任何负数和0都表示加锁*/volatile unsigned int slock;
#ifdef CONFIG_DEBUG_SPINLOCKunsigned magic;
#endif
#ifdef CONFIG_PREEMPT/*** 表示进程正在忙等待自旋锁。* 只有内核支持SMP和内核抢占时才使用本标志。*/unsigned int break_lock;
#endif
} spinlock_t;

spin_lock()

/*** 当内核不可抢占时,spin_lock的实现过程。*/
#define _spin_lock(lock)    \
do { \/*** 调用preempt_disable禁用抢占。*/preempt_disable(); \/*** _raw_spin_lock对自旋锁的slock字段执行原子性的测试和设置操作。*/_raw_spin_lock(lock); \__acquire(lock); \
} while(0)

函数_raw_spin_lock()对自旋锁的SLOCK字段执行原子性的测试和设置操作。

#define _raw_spin_lock(x)        \do { \CHECK_LOCK(x); \if ((x)->lock&&(x)->babble) { \(x)->babble--; \printk("%s:%d: spin_lock(%s:%p) already locked by %s/%d\n", \__FILE__,__LINE__, (x)->module, \(x), (x)->owner, (x)->oline); \} \(x)->lock = 1; \(x)->owner = __FILE__; \(x)->oline = __LINE__; \} while (0)

spin_unlock()

#define _spin_unlock(lock) \
do { \_raw_spin_unlock(lock); \preempt_enable(); \__release(lock); \
} while (0)
static inline void _raw_spin_unlock(spinlock_t *lock)
{
#ifdef CONFIG_DEBUG_SPINLOCKBUG_ON(lock->magic != SPINLOCK_MAGIC);BUG_ON(!spin_is_locked(lock));
#endif__asm__ __volatile__(spin_unlock_string);
}

宏函数spin_unlock_string

在spin_unlock_string中,%0即为 锁 - > s 锁,movb指令将锁 - > s 锁定为1,movb指令本身就是原子操作,所以不需要锁总线。

#define spin_unlock_string \"movb $1,%0" \:"=m" (lock->slock) : : "memory"

自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只有一个线程位于临界区内,这就为多处理器机器提供了防止并发访问所需的保护机制。

在单处理机器上,编译的时候不会加入自旋锁,仅会被当作一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占,那么在编译时自旋锁就会被剔除出内核。

内核提供的禁止中断同时请求锁的接口

(1)_spin_lock_irqsave()

保存中断的当前状态,并禁止本地中断,然后再去获取指定的锁。

unsigned long __lockfunc _spin_lock_irqsave(spinlock_t *lock)
{unsigned long flags;local_irq_save(flags);preempt_disable();_raw_spin_lock_flags(lock, flags);return flags;
}

(2)_spin_unlock_irqrestore()

对指定的锁解锁,然后让中断恢复到加锁前的状态

void __lockfunc _write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
{_raw_write_unlock(lock);local_irq_restore(flags);preempt_enable();
}

如果能确定中断在加锁前是激活的,那就不需要在解锁后恢复中断以前的状态。也就可以无条件地在解锁时激活中断。这时可以使用spin_lock_irq()和spin_unlock_irq()。

_spin_lock_irq()

禁止本地中断并获取指定的锁 。

void __lockfunc _read_lock_irq(rwlock_t *lock)
{local_irq_disable();preempt_disable();_raw_read_lock(lock);
}

_spin_unlock_irq()

释放指定的锁,并激活本地中断。

void __lockfunc _spin_unlock_irq(spinlock_t *lock)
{_raw_spin_unlock(lock);local_irq_enable();preempt_enable();
}

在使用spin_lock_irq()方法时,需要确定中断原来是否处于激活状态。一般不建议使用。

spin_lock_init()

动态初始化指定的spinlock_t,(此时只有一个指向spinlock_t类型地指针,没有它的实体)

#define spin_lock_init(lock) do { (void)(lock); } while(0)

spin_try_lock()

试图获的某个特定的自旋锁。如果该锁已经被争用,那么该函数立即返回一个非0值,而不会自旋等待锁被释放;

如果成功地获得了这个自旋锁,该函数返回0。

int __lockfunc _spin_trylock(spinlock_t *lock)
{preempt_disable(); //使抢占计数加1if (_raw_spin_trylock(lock))return 1;preempt_enable(); // 使抢占计数减1,并在thread_info描述符的TIF_NEED_RESCHED标志被置为1的情况下,调用preempt_schedule()return 0;
}

spin_is_locked()

用于检查特定的锁当前是否已被占用,如果已被占用,返回非0值;否则返回0。

#define spin_is_locked(x) \({ \CHECK_LOCK(x); \if ((x)->lock&&(x)->babble) { \(x)->babble--; \printk("%s:%d: spin_is_locked(%s:%p) already locked by %s/%d\n", \__FILE__,__LINE__, (x)->module, \(x), (x)->owner, (x)->oline); \} \0; \})

-------------------------------------------------- ----------------

总结

(1) 一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋,会特别浪费处理器时间。所以自旋锁不应该被长时间持有。因此,自旋锁应该使用在:短时间内进行轻量级加锁。

(2)还可以采取另外的方式来处理对锁的争用:让请求线程睡眠,直到锁重新可用时再唤醒它这样处理器不必循环等待,可以执行其他任务。

但是让请求线程睡眠的处理也会带来一定开销:会有两次上下文切换,被阻塞的线程要换出和换入所以,自旋持有锁的时间最好小于完成两次上下文e月刊的耗时,也就是让持有自旋锁的时间尽可能短。(在抢占式内核中,的锁持有等价于系统-的调度等待时间),信号量可以在发生争用时,等待的线程能投入睡眠,而不是旋转。

(3)在单处理机器上,自旋锁是无意义的。因为在编译时不会加入自旋锁,仅仅被当作一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占,那么在编译时自旋锁会被完全剔除出内核。

(4)Linux内核中,自旋锁是不可递归的。如果试图得到一个你正在持有的锁,你必须去自旋,等待你自己释放这个锁。但这时你处于自旋忙等待中,所以永远不会释放锁,就会造成死锁现象。

(5)在中断处理程序中,获取锁之前一定要先禁止本地中断(当前处理器的中断),否则,中断程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。这样就会造成双重请求死锁(中断处理程序会自旋,等待该锁重新可用,但锁的持有者在这个处理程序执行完之前是不可能运行的)

(6)锁真正保护的是数据(共享数据),而不是代码。对于BLK(大内核锁)保护的是代码。

补充:

BLK:大内核锁

BLK是一个全局自旋锁,主要目的是使Linux的最初的SMP过渡到细粒度加锁机制。

特性如下:

·持有BLK的任务可以睡眠的,是安全的。因为当任务无法被调度时,所加锁会自动被丢弃;当任务被调度时,锁会被重新获得。

·BLK是一种递归锁。一个进程可以多次请求一个锁,而不会像自旋锁那样造成死锁现象。

·BLK只可以用在进程上下文中。不同于自旋锁可在中断上下文中加锁。

·BLK锁保护的是代码。

参考:

《 Linux的内核设计与实现 》

Linux的内核中的互斥操作(2) - 自旋锁

宏函数spin_lock_string

如果能确定中断在加锁前是激活的,那就不需要在解锁后恢复中断以前的状态。也就可以无条件地在解锁时激活中断。这时可以使用spin_lock_irq()和spin_unlock_irq()。

_spin_lock_irq()

禁止本地中断并获取指定的锁 。

-------------------------------------------------- ----------------

总结

Linux内核同步方法——自旋锁(spin lock)相关推荐

  1. mysql从库读取数据轮训,1.1.17 控制自旋锁Spin Lock轮训间隔

    1.1.17 控制自旋锁Spin Lock轮训间隔 在介绍该特性之前,先来了解一下何谓自旋锁?它是为保护共享资源而提出的一种锁机制.其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用的 ...

  2. linux内核自旋锁解释,LINUX内核笔记:自旋锁

    目录 1.自旋锁作用与基本使用方法? 与其他锁一样,自旋锁也用于保护临界区,但是自旋锁主要是用于在SMP上保护临界区.在SMP上,自旋锁最多只能被一个可执行线程持有,如果一个线程尝试获得一个被争用的自 ...

  3. linux内核的自旋锁

    前言 感谢宋老师. 自旋锁(Spin Lock)是一种典型的对临界资源进行互斥访问的手段,其名称来源于它的工作方式.为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(T ...

  4. Linux内核同步 - Read/Write spin lock

    一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...

  5. 内核并发控制---自旋锁(来自网易)

    定义在头文件linux/spinlock.h中; 自旋锁(spin lock)是一种对临界资源进行互斥访问的典型手段;为了获得一个自旋锁,在某CPU上运行的代码需要首先执行一个原子操作,该操作测试并设 ...

  6. linux并发控制之自旋锁

    自旋锁是一种对临界资源进行互斥访问的典型手段,其名来源于它的工作方式. 通俗的讲,自旋锁就是一个变量,该变量把一个临界区标记为"我当前在运行,请等待"或者标记为"我当前不 ...

  7. linux 内核连接跟踪,Linux内核连接跟踪锁的优化分析(1)

    Linux内核连接跟踪锁的优化分析(1) 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 微博:weibo.com/glinuxer ...

  8. [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析

    关于进程上下文,中断上下文,请看这篇文章 Linux进程上下文和中断上下文内核空间和用户空间 自旋锁的初衷:在短期间内进行轻量级的锁定.一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋 ...

  9. 【Linux kernel】自旋锁和互斥锁

    内核当发生访问资源冲突的时候,可以有两种锁的解决方案选择: 一个是原地等待 一个是挂起当前进程,调度其他进程执行(睡眠) Linux内核提供了自旋锁和互斥锁的机制,两者都能保证在同一时刻只有一个执行单 ...

最新文章

  1. 用Java中的HashSet写一个学生管理系统(添加、删除、修改、查询学生信息)
  2. SAP RETAIL 事务代码RWBE查询界面上的Plant Group
  3. 顶级项目管理工具 Top 10
  4. linux x和s 的区别,Hadoop1.x和2.X的HDFS fsimage和edits文件运行机制对比
  5. c语言指针变量的定义数组的长度,【C语言更新】指向数组的指针
  6. 使用爬虫下载x书视频
  7. WiFi无缝漫游详解
  8. 苹果手机上怎么打开html,苹果手机信息怎么打开浏览器
  9. html缩小照片尺寸像素不变,怎么修改照片像素,但又不改变照片大小呢?——解决照片因大小无法上传的方案...
  10. 平安科技测试面试经验分享
  11. SSL-ZYC 游戏
  12. 用户登陆成功修改SessionId
  13. Redis-desktop-manager显示黑屏
  14. Redis主从配置详细流程
  15. 智慧景区视频监控方案
  16. 数据中台总体技术架构
  17. 如何有效防止系统邮件被视为垃圾邮件
  18. 编程基础知识(变简单的进制转换)
  19. 用java编写博弈树_MathorCup竞赛优秀论文基于MonteCarlo局面评估和UCT博弈树搜索的...
  20. 通网网站被大规模挂马 15个频道中病毒

热门文章

  1. 国培计算机培训返岗总结,国培计划返岗实践总结
  2. 芒果iOS开发人事面试问题及答案汇总二
  3. 全局变量和局部变量初始值
  4. crtsiii型无砟轨道板_CRTS-I,CRTS-II,CRTS-III型轨道板有啥区别?
  5. 索尼爱立信哪款手机java最强,急!!!手机索尼爱立信S500C与三星SGH-E958哪款好?...
  6. 分享第一次教资面试经历,没过就五月见~加油
  7. 毕业季.进击的技术——20岁的年纪,只管大方勇敢向前
  8. 武侠争霸·英雄帖在此,少侠留步
  9. DNS 域名解析服务
  10. 【CPU风扇不转怎么办】