参考: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的更多相关文章

  1. BKL 大内核锁

    BKL 大内核锁 BKL是一种递归锁.一个进程可以多次请求一个锁,并不会像自旋锁那么产生死锁. BKL可以在进程上下文中. BKL是有害的. 在内核中不鼓励使用BKL.一个执行线程可以递归的请求锁lo ...

  2. Linux内核中锁机制之RCU、大内核锁

    在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...

  3. 大话Linux内核中锁机制之RCU、大内核锁

    大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核 ...

  4. Linux内核锁与中断处理

    Linux内核锁 在Linux内核里面,一般采用了如下几种锁的机制,来保证多线程的同步与互斥: (1)原子操作 atomic_t v: void atomic_set(atomic_t *v, int ...

  5. 20169210《Linux内核原理与分析》第七周作业

    第一部分:实验 首先还是网易云课堂的实验内容,扒开系统调用的三层皮(下),分为两部分: 1.给MenuOS增加time和time-asm命令 2.系统调用在内核代码中的处理过程 给MenuOS增加ti ...

  6. Linux内核同步

    Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...

  7. 20179223《Linux内核原理与解析》第六周学习笔记

    视频知识学习 给MenuOS增加time和time-asm命令 1.更新menu代码到最新版 2.再main()函数中增加MenuConfig 3.增加对应的Time函数和TimeAsm函数(这里的函 ...

  8. Linux内核设计与实现 总结笔记(第十章)内核同步方法

    一.原子操作 原子操作可以保证指令以原子的方式执行----执行过程不被打断. 1.1 原子整数操作 针对整数的原子操作只能对atomic_t类型的数据进行处理. 首先,让原子函数只接收atomic_t ...

  9. kernel笔记——内核同步与锁

    内核同步 内核同步解决并发带来的问题,多个线程对同一数据进行修改,数据会出现不一致的情况,同步用于保护共享数据等资源. 有两种形式的并发: 同时进行式并发,在不同cpu上执行的进程同时访问共享数据 二 ...

随机推荐

  1. Sublime Text 3安装与使用,安装插件,快捷键,默认配置

    本文是Sublime Text 全程指引 by Lucida (http://www.cnblogs.com/figure9/p/sublime-text-complete-guide.html)的笔 ...

  2. Java 数据库中文变成问号???解决办法

    在连接的URL地址后面加上: url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 于是在正式 ...

  3. Mac下配置Apache Httpd的Https/SSL

    Mac下配置Apache Httpd的Https/SSL httpd版本: httpd-2.4.17 jdk版本: jdk1.8.0_65 参考来源: Mac下安装Apache Httpd Mac O ...

  4. 关于Linux系统指令 top 之 %si 占用高,分析实例一

    续“top %wa 高的问题”之后,又遇到top之%si过高(高峰时段超过95%)的问题. %wa高,说明磁盘忙.譬如磁盘读写次数非常高.%si高,是否说明软中断忙?是否也说明软中断次数非常高呢? 为 ...

  5. 大数据处理-Bitmap

    MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算.概念"Map(映射)"和"Reduce(归约)" Bit-map空间压缩和快速排序去 ...

  6. Spring Boot Gradle 打包可执行Jar文件!

    使用Gradle构建项目,继承了Ant的灵活和Maven的生命周期管理,不再使用XML作为配置文件格式,采用了DSL格式,使得脚本更加简洁. 构建环境: jdk1.6以上,此处使用1.8 Gradle ...

  7. CREATESTRUCT cs 结构体

    PreCreateWindow(CREATESTRUCT& cs) typedef struct tagCREATESTRUCT { LPVOID lpCreateParams; // 创建窗 ...

  8. jQuery源码学习扒一扒jQuery对象初使化

    神奇的jQuery可以这样玩jQuery("#id").css()或 jQuery("#id").html() 这么玩jQuery("#id" ...

  9. jquery-仿flash的一个导航栏特效

    演示地址:http://itxiaoming.sinaapp.com/demo05/demo.html <html> <head> <meta http-equiv=&q ...

  10. A SIMPLE LIBRARY TO BUILD A DEEP ZOOM IMAGE

    My current project requires a lot of work with Deep Zoom images. We recently received some very high ...