内核并发控制---自旋锁(来自网易)
定义在头文件linux/spinlock.h中;
自旋锁(spin lock)是一种对临界资源进行互斥访问的典型手段;为了获得一个自旋锁,在某CPU上运行的代码需要首先执行一个原子操作,该操作测试并设置某个内存变量,由于该操作是原子操作,所以在该操作完成之前,其它执行单元对该内存变量的访问被禁止;
如果测试结果表明该自旋锁已经空闲,则程序获得这个自旋锁并继续运行;如果测试结果表明该自旋锁仍然被占用,那么,程序将在一个小的循环内重复执行这个"测试并设置"操作,即,进行所谓的"自旋",通俗地讲就是"在原地打转".当该自旋锁的持有者通过重置该变量释放这个自旋锁之后,某个等待的"测试并设置"操作向其它调用者报告该所已经释放;
理解自旋锁最简单的方法就是把它看成一个变量,该变量把一个临界区或者标记为"我当前正在运行,请稍等一会",或者标记为"我当前不在运行,可以被使用";如果A执行单元首先进入临界区,那么,它将获得并持有该自旋锁;当B执行单元试图进入同一个临界区的时候,将获知该自旋锁已经被持有,需要等到A执行单元释放该自旋锁之后才能进入该临界区;
自旋锁使得设备最多只被一个进程打开;
自旋锁的相关操作:
1).定义自旋锁:
spinlock_t spin;
2).初始化自旋锁:
spin_lock_init(lock);
该函数用于动态初始化自旋锁lock
3).获得自旋锁:
spin_lock(lock);
该函数用于获得自旋锁lock,如果能够立即获得自旋锁,它将马上返回,否则,它将自旋在那里,直到该自旋锁被持有者释放为止;
spin_trylock(lock);
该函数尝试获得自旋锁,如果能够立即获得自旋锁,那么它将得到并持有自旋锁并马上返回真,否则立即返回假,实际上不再"在原地打转"了;
4).释放自旋锁
spin_unlock(lock);
该函数用于释放自旋锁lock;它与spin_lock()或spin_trylock()配对使用;
自旋锁的使用套路:
spinlock_t lock; //定义自旋锁
spin_lock_init(&lock); //初始化自旋锁
spin_lock(&lock); //获得自旋锁,保护临界区;
......
//临界区代码
......
spin_unlock(&lock); //释放自旋锁
自旋锁主要是针对SMP和单CPU但内核支持可抢占式调度的系统;对于单CPU和内核不支持可抢占式调度的系统来说,自旋锁就退化为空操作了;在单CPU和内核可抢占的系统中,自旋锁被持有期间,内核的抢占被禁止;由于内核可抢占的单CPU系统的行为实际很类似于SMP系统,因此,在这样的单CPU系统中使用自旋锁仍十分必要;
尽管使用了自旋锁可以保证临界区不会受到其它CPU和本CPU内抢占进程的打扰,但是得到自旋锁的代码路径在执行临界区代码的时候仍然可能会被中断和低半部所影响;为了防止这种影响,就需要用到自旋锁的衍生;spin_lock()和spin_unlock()是自旋锁机制的基础;把自旋锁与中断屏蔽、原子操作等手段联合使用,就可以形成一整套完整的自旋锁机制;
关系如下:
spin_lock_irq() = spin_lock() + local_irq_disable();
spin_unlock_irq() = spin_unlock() + local_irq_enable();
spin_lock_irqsave() = spin_lock() + local_irq_save();
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore();
spin_lock_bh() = spin_lock() + local_bh_disable();
spin_unlock_bh() = spin_unlock() + local_bh_enable();
5).驱动程序工程师应该谨慎使用自旋锁,而且在使用中还要注意以下几个问题:
A.自旋锁实际上是忙等待;当自旋锁不可用的时候,CPU就一直重复循环执行"测试并设置"该锁,直到可用并获得锁为止;CPU在等待自旋锁的时候不做任何有用的事情,仅仅是忙等待而已;因此,只有在占用锁的时间极短的情况下使用自旋锁才是合理的;当临界区范围很大或者是有共享资源的时候,需要较长时间占用自旋锁,这个时候使用自旋锁会降低系统性能;
B.自旋锁可能导致系统死锁;引发这种问题的一个最常见的情况就是递归地使用一个自旋锁,比如:如果一个已经拥有某个自旋锁的CPU想再次获得这个自旋锁,则该CPU就会陷入死锁状态;此外,如果进程获得自旋锁之后进入阻塞状态,也有可能会导致该进程陷入死锁状态;copy_from_user()、copy_to_user()和kmalloc()等函数都有可能会引起阻塞而导致陷入死锁状态,因此在自旋锁被占用期间不能调用这些函数;
6).其它函数/宏:
spin_is_locked(spinlock):
该宏用于判断自旋锁spinlock是否已经被某执行单元所持有(锁定);如果是,则返回真,否则返回假;
spin_unlock_wait(spinlock):
该宏用于等待自旋锁spinlock变得没有被任何执行单元所持有,也即:等待自旋锁spinlock被所有持有该锁的执行单元释放;如果所有持有自旋锁spinlock的执行单元都已经释放该锁(即:自旋锁spinlock不再被任何执行单元所持有),则该宏立即返回,否则就循环在那里,直到该锁被所有持有该锁的执行单元释放为止;
例子:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
//这三个头文件与内核线程的使用有关;
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
//自旋锁相关
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("*************");
MODULE_VERSION("2.6.35.000");
static int sleep_time = (1*10*HZ);
static int shared_res = 0;
//STEP1:定义自旋锁
spinlock_t my_spin_lock;
//STEP5:实现线程函数
static int thread_process1(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
spin_lock(&my_spin_lock);
shared_res++;
//STEP4:对临界区解锁
spin_unlock(&my_spin_lock);
mdelay(sleep_time);
}
return 12;
};
static int thread_process2(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
spin_lock(&my_spin_lock);
shared_res++;
//STEP4:对临界区解锁
spin_unlock(&my_spin_lock);
msleep(sleep_time);
}
return 34;
};
static int thread_process3(void* param)
{
int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
spin_lock(&my_spin_lock);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
//STEP4:对临界区解锁
spin_unlock(&my_spin_lock);
msleep(sleep_time);
}
return 56;
};
static int thread_process4(void* param)
{
int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
spin_lock(&my_spin_lock);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
//STEP4:对临界区解锁
spin_unlock(&my_spin_lock);
msleep(sleep_time);
}
return 78;
};
static struct task_struct* my_thread1 = NULL;
static struct task_struct* my_thread2 = NULL;
static struct task_struct* my_thread3 = NULL;
static struct task_struct* my_thread4 = NULL;
static int __init study_init(void)
{
int err = 0;
printk("%s\n", __PRETTY_FUNCTION__);
//STEP2:初始化自旋锁
spin_lock_init(&my_spin_lock);
printk("init spin lock ok\n");
my_thread1 = kthread_create(thread_process1, NULL, "my_thread1");
if(IS_ERR(my_thread1))
{
err = PTR_ERR(my_thread1);
my_thread1 = NULL;
printk(KERN_ERR "unable to start kernel thread1:%d\n", err);
return err;
}
my_thread2 = kthread_create(thread_process2, NULL, "my_thread2");
if(IS_ERR(my_thread2))
{
err = PTR_ERR(my_thread2);
my_thread2 = NULL;
printk(KERN_ERR "unable to start kernel thread2:%d\n", err);
return err;
}
my_thread3 = kthread_create(thread_process3, NULL, "my_thread3");
if(IS_ERR(my_thread3))
{
err = PTR_ERR(my_thread3);
my_thread3 = NULL;
printk(KERN_ERR "unable to start kernel thread3:%d\n", err);
return err;
}
my_thread4 = kthread_create(thread_process4, NULL, "my_thread4");
if(IS_ERR(my_thread4))
{
err = PTR_ERR(my_thread4);
my_thread4 = NULL;
printk(KERN_ERR "unable to start kernel thread4:%d\n", err);
return err;
}
wake_up_process(my_thread1);
wake_up_process(my_thread2);
wake_up_process(my_thread3);
wake_up_process(my_thread4);
printk("%s:all kernel thread start;\n", __FUNCTION__);
return 0;
}
static void __exit study_exit(void)
{
int ret = -1;
printk("%s\n",__PRETTY_FUNCTION__);
if(my_thread1)
{
ret = kthread_stop(my_thread1);
my_thread1 = NULL;
printk("kernel thread1 stop,exit code is %d;\n",ret);
}
if(my_thread2)
{
ret = kthread_stop(my_thread2);
my_thread2 = NULL;
printk("kernel thread2 stop,exit code is %d;\n",ret);
}
if(my_thread3)
{
ret = kthread_stop(my_thread3);
my_thread3 = NULL;
printk("kernel thread3 stop,exit code is %d;\n",ret);
}
if(my_thread4)
{
ret = kthread_stop(my_thread4);
my_thread4 = NULL;
printk("kernel thread4 stop,exit code is %d;\n",ret);
}
printk("%s:all kernel thread stop;\n", __FUNCTION__);
}
module_init(study_init);
module_exit(study_exit);
内核并发控制---自旋锁(来自网易)相关推荐
- 内核并发控制---信号量 (来自网易)
定义在头文件linux/semaphore.h中; 信号量(semaphore)是用于保护临界区的一种常用方法,它的使用方式与自旋锁类似;与自旋锁相同,只有得到信号量的进程才能执行临界区的代码;但是, ...
- 内核并发控制---中断屏蔽 (来自网易)
定义在头文件linux/irqflags.h中; 在单CPU内部避免竞态的一种方法是在进入临界区之前先屏蔽系统的中断,离开临界区之前再恢复系统中断;CPU一般都应该具有打开中断和关闭中断的功能;这项功 ...
- 内核并发控制---原子操作(来自网易)
定义在头文件asm/atomic.h中; 原子操作指的是在执行过程中不会被别的代码路径所打断的操作; Linux内核提供了一系列的函数来实现内核中的原子操作,这些函数又分为两类,分别针对位变量和整型变 ...
- [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析
关于进程上下文,中断上下文,请看这篇文章 Linux进程上下文和中断上下文内核空间和用户空间 自旋锁的初衷:在短期间内进行轻量级的锁定.一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋 ...
- 内核并发控制---顺序锁 (来自网易)
定义在头文件linux/seqlock.h中; 顺序锁(seqlock)是对读写锁的一种优化,若使用顺序锁,读执行单元绝对不会被写执行单元所阻塞,也就是说,读执行单元可以在写执行单元对被顺序锁保护的共 ...
- 设备驱动中的并发控制-自旋锁
在linux中提供了一些锁机制来避免竞争,引入锁的机制是因为单独的原子操作不能满足复杂的内核设计需求.Linux中一般可以认为有两种锁,一种是自旋锁,另一种是信号量.这两种锁是为了解决内核中遇到的不同 ...
- linux内核自旋锁解释,LINUX内核笔记:自旋锁
目录 1.自旋锁作用与基本使用方法? 与其他锁一样,自旋锁也用于保护临界区,但是自旋锁主要是用于在SMP上保护临界区.在SMP上,自旋锁最多只能被一个可执行线程持有,如果一个线程尝试获得一个被争用的自 ...
- Linux内核同步方法——自旋锁(spin lock)
自旋锁 Linux的的内核最常见的锁是自旋锁.自旋锁最多只能被一个可执行线程持有.如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁重新可用要是锁未被争 ...
- linux内核的自旋锁
前言 感谢宋老师. 自旋锁(Spin Lock)是一种典型的对临界资源进行互斥访问的手段,其名称来源于它的工作方式.为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(T ...
最新文章
- 干货 | 你还在群发吗?高效查出哪些微信好友删除了你
- ACwing 245. 你能回答这些问题吗(线段树区间子段最大值+单点修改)
- HTML转存问题测试
- Spring @RequestParam批注
- python3-pandas 缺失数据的处理
- android屏幕亮度测试,屏幕亮度与可视角度测试_手机Android频道-中关村在线
- 浅谈Tomcat接收到一个请求后在其内部的执行流程(源码)
- Linux系统如何安装PDF编辑器,在Ubuntu中编辑PDF文件的5种方法
- treetable怎么带参数_treeTable的使用(ajax异步获取数据,动态渲染treeTable)
- python遍历文件夹以及排序问题
- WIN10重置网络后网络适配器不见了并且适配器驱动感叹号
- pandas 选取行和列的方法
- 学习少儿编程成就不平凡人生
- java 网速测试_简易的网速测试 - 梦想游戏 - OSCHINA - 中文开源技术交流社区
- html隐藏域保存数组,关于给JS组合数组赋值给隐藏域问题
- jQuery 加入购物车 弹窗
- 基于激活聚类的后门检测:Detecting Backdoor Attacks on Deep Neural Networks by Activation Clustering
- 2022年华数杯C题插层熔喷非织造材料的性能控制研究数学建模论文及程序
- 什么是放大器和宽带放大器及其性能概述
- Python-opencv 读取图片RGB/HSI通道值
热门文章
- pytorch构造IterableDataset,流式读取文件夹,文件夹下所有大数据文件,逐个文件!逐行读取!(pytorch Data学习四)
- android studio创建一个类继承application_带你全方位了解Android中的Context
- java对象克隆的例子_Java对象克隆
- 文件操作,列表实例NiceHexSpiral
- Direct2D 如何关闭抗锯齿
- 20145226夏艺华 《Java程序设计》实验报告一
- uva10026-鞋匠的难题
- IFrame中 Forms验证超时页面跳转跳出框架 简单解决方法
- java常量池方法区_Java方法区和运行时常量池溢出问题分析
- 将一个类改成线程_看了这个有趣的例子,相信你就秒懂多线程同步了