参考: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. JAVA-数据库之加载JDBC驱动程序

    相关资料:<21天学通Java Web开发> 加载JDBC驱动程序 JiaZaiDemo.jsp <%@ page language="java" content ...

  2. ISO8601时间格式

    格式解析 R2/2015-06-04T19:25:16.828696-07:00/P1DT10S 上面的字符串通过"/"分为了三部分即: 重复次数/开始时间/运行间隔 重复次数 R ...

  3. 了解PHP中$_SERVER变量对路径的解析

    1,$_SERVER["QUERY_STRING"]说明:查询(query)的字符串 2,$_SERVER["REQUEST_URI"]说明:访问此页面所需的U ...

  4. 手写体识别中用到的Tensorflow函数复习

    tf.truncated_normal(shape, stddev=0.1) 从截断的正态分布中输出随机值. 生成的值服从具有指定平均值和标准偏差的正态分布,如果生成的值大于平均值2个标准偏差的值则丢 ...

  5. C语言 · 字串逆序

    算法训练 字串逆序   时间限制:1.0s   内存限制:512.0MB      问题描述 给定一个字符串,将这个串的所有字母逆序后输出. 输入格式 输入包含一个字符串,长度不超过100,字符串中不 ...

  6. Postgresql查询表的大小

    --数据库中单个表的大小(不包含索引) select pg_size_pretty(pg_relation_size('表名')); --查出所有表(包含索引)并排序 SELECT table_sch ...

  7. hadoop rebalance

    之前一直没做过rebalance,以为速度很快,结果大意了,等到磁盘达到90%的时候,才开始做rebalance. 默认的从日志中可以看到总共需要迁移1.89T,但是每次只移动40G大小的量. 然后查 ...

  8. linux软件管理(六)

    [教程主题]:linux软件管理 [1]软件包管理 在系统管理中,软件包的管理是最重要的,是系统管理的基础的基础, 只有我们学会软件包的管理才能谈得上其它的应用. RPM RPM是软件管理程序,提供软 ...

  9. ambari删除脚本

    #.删除hdp.repo和hdp-util.repo cd /etc/yum.repos.d/ rm -rf hdp* rm -rf HDP* #rm -rf ambari* #.删除安装包 #用yu ...

  10. 【驱动】——错误: 初始值设定项里有未知的字段‘ioctl’

    这个错误网上搜索发现3.0.0.15版本内核 file_operation结构体已经删除了ioctl函数,取代的是: long (*unlocked_ioctl) (struct file *, un ...