大内核锁 BKL
参考:http://blog.csdn.net/universus/article/details/5623971
http://blog.csdn.net/chenyu105/article/details/7726492
《Linux Kernel Development》 3ed_CN p159
2.6.34
锁保护的是数据,在程序路径中访问数据,确保数据被访问的惟一性,就必须保证只有一个线程正在位于相应的程序路径。
大内核锁也是一种锁,问题在于其锁的粒度太粗,当有大量不同的数据在一个程序路径中被访问时,若能保证只有一个线程位于该程序路径,则可以保证数据正在被访问的惟一性。大内核锁的意义在于,线程要想进入某个程序路径来访问数据,就必须获得该锁,表面上看就像是大内核锁保护了程序路径,其它线程无法进入程序路径。
内核中哪一处锁住了大量的数据?如下:
//此处以2.6.34为准,3.10.5中在内核初始化时没有使用这种机制
void start_kernel(void)
| 此处只关注BKL
|---->local_irq_disable();
|---->lock_kernel();
|---->local_irq_enable();
|---->rest_init()
|---->unlock_kernel();
#define lock_kernel() do { \
_lock_kernel(__func__, __FILE__, __LINE__); \
}while(0)
#define unlock_kernel() do { \
_unlock_kernel(__func__, __FILE__, __LINE__); \
} while ()
_lock_kernel的实现:
void __lockfunc _lock_kernel(const char *func, const char *file, int line)
|---->int depth = current->lock_depth + ;
| 注意:新建进程时在copy_process中将新建的进程的lock_depth设置成-
|
|如果上首次进行lock_kernel,则尝试获取这把锁
|if (likely(!depth))
|{
| might_sleep();
| __lock_kernel();
|}
|
|---->current->lock_depth = depth; __lock_kernel()
static inline void __lock_kernel(void)
|---->preempt_disable()禁止抢占
|---->do_raw_spin_lock(&kernel_flag); 此以上可以看出,对于lock_kernel实际上就是将current->lock_depth值加1,为了防止死锁,只在首次进行lock_kernel时preempt_disable(),同时do_raw_spin_lock(&kernel_flag),相当于spin_lock,只是由于数据类型不同而分成两部完成。
持有BKL的进程可以睡眠,BKL支持嵌套加锁解锁,为了支持该特性,在task_struct中专门为BKL设置了加锁计数器lock_depth域。 问题在于,既然我们有新的机制以充分发挥SMP的性能,为何不把BKL剔除?
原因:因为持有BKL的执行路径可以睡眠,可以嵌套对BKL加锁解锁,而spin_lock不支持这样的机制,如果将BKL彻底踢出而换成BKL机制,由于无法完整考察在相应的程序路径中是否调用了schedule、是否嵌套获取锁,因此很容易造成内核死锁。
在schedule中进行进程切换时,会检查当前进程是否持有大内核锁,以及在进程重新得到CPU使用权时会检查自身原来是否持有自旋锁。
release_kernel_lock会判断如果当前进程持有大内核锁,则preempt_enable()并释放锁。
reacquire_kernel_lock在进程再次被调度回来后,检查当前进程在切换之前是否持有大内核锁。如果持有的话,说明在进程切换时,当前进程的大内核锁被强行释放了,需要再次获取:获取锁、preempt_disable();但是我们也应注意此处与spin_lock的区别,spin_lock时若不能或的锁则一直自旋,而reacquire_kernel_lock中会不断地尝试获取自旋锁,每次没有获取成功时都需要检查是否需要放弃CPU使用权。 void schedule(void)
|详细的请参考笔记:http://www.cnblogs.com/openix/p/3272406.html
|此处重点说明对大内核锁的处理
|
|---->release_kernel_lock(prev);
|---->if (unlikely((tsk)->lock_depth >= ))
| __release_kernel_lock();
| |---->do_raw_spin_unlock(&kernel_flag);
| |---->preempt_enable_no_resched();
|---->context_swith();
|---->reacquire_kernel_lock(current);
|---->if (unlikely(task->lock_depth >= ))
| return __reacquire_kernel_lock();
| |----while (!do_raw_spin_trylock(&kernel_flag)) {
| |---- if (need_resched())
| |---- return -EAGAIN; //重新调度
| |----}
| |----preempt_disable();
| |---->return ;
小结:
用等同于spinlock_t的数据结构来实现大内核锁(实际是raw_spinlock_t);
__lock_kernel()等同于spin_lock()
__unlock_kernel()等同于spin_unlock()
__release_kernel_lock()近似于同于spin_unlock() (没有检查自身是否需要放弃CPU使用权的检查)
___reacquire_kernel_lock()近似于spin_lock() (但是在调用过程中检查是否需要放弃CPU使用权)
同时也相当于用spin_lock与spin_unlock的方式来获取锁(尽管实际上并非如此,但是对于2.6.34而言,这是等同的)。
持有大内核锁的用户代码可以睡眠:进程切换时会检查当前进程是否持有大内核锁,而采取释放核重获的操作。
附:引入大内核锁的原因。 请参考两处链接中的内容
早期引入大内核锁的原因:
引入大内核锁以支持SMP处理器,在内核入口设置BKL,一旦一个处理器进入内核态就立刻上锁。由于只有一个处理器在运行内核态运行,内核的执行本质上和单处理器没有什么区别。进入该机制的缺点:多处理器的性能只能体现在用户态的并行处理上,而在内核态还是单线执行,不能挖掘SMP的性能。由于内核大部分代码是多处理器安全的,只有少数全局资源具有互斥性,所以所有处理器都可以随时进入内核态执行,关键在于需要把具有排它性的资源找出,并在访问这些资源时加以保护。这样,大内核锁从保护整个内核态缩小为零散地保护内核态的某些关键数据
大内核锁 BKL的更多相关文章
- BKL 大内核锁
BKL 大内核锁 BKL是一种递归锁.一个进程可以多次请求一个锁,并不会像自旋锁那么产生死锁. BKL可以在进程上下文中. BKL是有害的. 在内核中不鼓励使用BKL.一个执行线程可以递归的请求锁lo ...
- Linux内核中锁机制之RCU、大内核锁
在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...
- 大话Linux内核中锁机制之RCU、大内核锁
大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核 ...
- Linux内核锁与中断处理
Linux内核锁 在Linux内核里面,一般采用了如下几种锁的机制,来保证多线程的同步与互斥: (1)原子操作 atomic_t v: void atomic_set(atomic_t *v, int ...
- 20169210《Linux内核原理与分析》第七周作业
第一部分:实验 首先还是网易云课堂的实验内容,扒开系统调用的三层皮(下),分为两部分: 1.给MenuOS增加time和time-asm命令 2.系统调用在内核代码中的处理过程 给MenuOS增加ti ...
- Linux内核同步
Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...
- 20179223《Linux内核原理与解析》第六周学习笔记
视频知识学习 给MenuOS增加time和time-asm命令 1.更新menu代码到最新版 2.再main()函数中增加MenuConfig 3.增加对应的Time函数和TimeAsm函数(这里的函 ...
- Linux内核设计与实现 总结笔记(第十章)内核同步方法
一.原子操作 原子操作可以保证指令以原子的方式执行----执行过程不被打断. 1.1 原子整数操作 针对整数的原子操作只能对atomic_t类型的数据进行处理. 首先,让原子函数只接收atomic_t ...
- kernel笔记——内核同步与锁
内核同步 内核同步解决并发带来的问题,多个线程对同一数据进行修改,数据会出现不一致的情况,同步用于保护共享数据等资源. 有两种形式的并发: 同时进行式并发,在不同cpu上执行的进程同时访问共享数据 二 ...
随机推荐
- JAVA-JSP内置对象之request获得封装所有参数值的Map
相关资料:<21天学通Java Web开发> 获得封装所有参数值的Map1.通过request对象的getParameterMap()方法来获得封装所有的参数值的Map对象.2.通过该Ma ...
- ARKit从入门到精通(9)-ARKit让飞机跟着镜头飞起来
1.1-ARKit物体跟随相机移动流程介绍 1.2-完整代码 1.3-代码下载地址 废话不多说,先看效果 1001.gif 1.1-ARKit物体跟随相机移动流程介绍 1.点击屏幕添加物体,已经在第三 ...
- Android基础——使用Fragment适应不同屏幕和分辨率
最近事情很忙,一个新项目赶着出来,但是很多功能都要重新做,一直在编写代码.Debug.今天因为一个新程序要使用Fragment来做,虽然以前也使用过Fragment,不过没有仔细研究,今天顺道写篇文章 ...
- [转]java中参数" ..."的用法和意思
原文地址:https://blog.csdn.net/lycit/article/details/78809625 如这个jdbc中封装的绑定参数的方法: /** * 绑定参数 * @param ps ...
- MEMCACHE分布式算法(PHP)
//测试分布式算法 $ports = array('端口1', '端口2', '端口3', '端口4', '端口5'); //Memcache端口 $users = array('1000', '10 ...
- C语言 · 求arccos值
算法提高 7-2求arccos值 时间限制:10.0s 内存限制:256.0MB 问题描述 利用标准库中的cos(x)和fabs(x)函数实现arccos(x)函数,x取值范围是[- ...
- c# comboBox输出图文效果
核心代码:重写DrawItem事件 void Event_CboDrawItem(object sender, DrawItemEventArgs e) { ) return; var cbo = s ...
- Codeforces Round #256 (Div. 2) B (448B) Suffix Structures
题意就是将第一个字符串转化为第二个字符串,支持两个操作.一个是删除,一个是更换字符位置. 简单的字符串操作!. AC代码例如以下: #include<iostream> #include& ...
- mysql国内镜像下载网址
http://mirrors.sohu.com/mysql/ http://mirrors.ustc.edu.cn/mysql-ftp/Downloads/ 开源镜像站点汇总 http://segme ...
- PCL的PNG文件和计算点云重心
PCL提供节约一点云的值为一个PNG图像文件的可能方案.显然,这只能用有序的点云来完成,因为生成的图像的行和列将与点云的对应完全一致.例如,如果你从一个传感器Kinect或Xtion的点云,你可以用这 ...