参考: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. 纯CSS3悬停图标旋转导航动画代码

    分享一款纯CSS3悬停图标旋转导航动画代码.这是一款鼠标移到图标上动画旋转显示导航菜单.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div id="x_con ...

  2. [转]Mustache 使用心得总结

    原文地址:https://blog.csdn.net/xuemoyao/article/details/17896203 前言: 之前的一个项目里面就有用到这个前台的渲染模版,当时挺忙的也没时间抽空总 ...

  3. DataTable的一些特殊用法:Select

    当你从数据库里取出一些数据,然后要对数据进行整合,你很容易就会想到: 1DataTable dt = new DataTable();//假设dt是由"SELECT C1,C2,C3 FRO ...

  4. [Linux]基本I/O重定向

    在我们设置定时任务的时候经常会使用标准输出和标准错误输出.这个在Linux是一个非常重要的概念,而且这个很有用.程序应该有数据库的来源端.数据的目的端,以及报告问题的地方,它们被称为标准输入.标准输出 ...

  5. Android——SQLite数据库(二)升级数据库、增、删、改、查、事务

    xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= ...

  6. windows 下 MySql5.6主从复制

    说明: 1.MySql 版本5.6 2.本例中使用的主从服务器ip分别为:192.168.120.211:192.168.120.209 一.配置master服务器 1.配置 在my.ini中[mys ...

  7. 莫衷一是——i+++j 该怎样计算?

    这是一个有趣的计算, 3 个加号相连.那么,究竟是怎样结合的呢?是依照: i + (++j)来运算,还是依照(i++) + j 来运算呢? 这个问题在相似于 C / C++中讨论是没有多大意义的,由于 ...

  8. python 输出当前行号

    import sys print sys._getframe().f_lineno ---------------------------------------------------------- ...

  9. 【C】——setvbuf(scanf内存溢出问题)

    下面设置了一个长度为20的缓存区,循环对value进行赋值并输出: #include<stdio.h> #define BUFSIZE 20 int main(int argc, char ...

  10. AutoCAD 导入 Altium Designer 10 教程

    http://www.geek-workshop.com/thread-5478-1-1.html