目录:
一、铺垫知识
1、指令执行流
2、上下文
3、抢占
二、内核锁基础知识
1、为什么要用锁?why
2、锁保护什么?what
3、锁是如何保护资源的?How
三、各类锁的介绍
1、原子操作
2、spinlock
3、mutex
4、…
![](https://img-blog.csdnimg.cn/4f1c81b88c2b4d69958484fcb8c859c1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_19,color_FFFFFF,t_70,g_se,x_16)
进程指令执行流
代码在CPU上执行的指令数据流,由一系列代码组成。可分为两大类:线程维度和中断维度
![](https://img-blog.csdnimg.cn/61003c0c2e624f439fa682a6860d1760.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_20,color_FFFFFF,t_70,g_se,x_16)
1、cpu只要上电,就需要不停的执行指令,永不停歇,若无事可做,那就执行空指令,直到下电。cpu类似一个跑道,各个task轮流到跑道上跑。
2、用户态视角:cpu执行一个个进程或者线程,一个线程就是一个执行流。内核态视角:CPU调度执行一个个task(对应一个进程或者线程),一个task就是一个执行流。CPU就这样永不疲倦的轮换执行各个task(调度)。8个CPU,同一时刻,最多有8个执行流。
3、并发:用户态系统调用的代码编译成so,当so被多个bin链接时,运行时,就可能有多个指令执行流,就有可能分别在cpu0和cpu1上执行。宏观并行,微观串行。
4、代码是静态的,执行流是动态的,这是两个不同的视角。若只存在一个cpu,那就只有一个执行流,那就不存在微观上的并发,也就不需要锁。但我们往往使用多个cpu,因而有多个执行流,同一个代码可能在两个执行流上执行。如上图。
上下文(context)
分类:进程上下文,中断上下文(硬中断上下文、软中断上下文,不可屏蔽中断上下文)
![](https://img-blog.csdnimg.cn/7e5f799abae64bf6a23b33cd6d048ff6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_9,color_FFFFFF,t_70,g_se,x_16)
中断能够打断进程的执行流,无论进程优先级多高,都会被打断。因此原本执行流被打断,CPU转而执行中断的指令流。因此,与上下文对应,执行流也可分为:进程执行流和中断执行流
1、cpu在用户态运行时,外部中断触发,程序会先陷入内核态,保存上下文后,再执行中断代码。
2、cpu在内核态运行时,外部中断触发,保存上下文后,执行中断代码。
3、中断处理函数(中断上半部)必须要快速执行完成,以便返回继续执行各个进程。中断返回时,会发生调度,原来被打断的进程未必能够得到执行。
假设进程和中断都调用如下函数,那么就有可能出现如下场景:
进程执行流:
1、某进程执行完743行后,全局变量enable=1;
2、外部触发中断。
程序执行流:
7、从744行继续执行,此时enable变成0了。后边就会出错
中断执行流:
3、中断处理函数恰好也调用这个函数;
4、走了case1的分支,将enable又改成0。
5、中断执行完毕返回。
抢占
什么是抢占? 一张图展现
![](https://img-blog.csdnimg.cn/c158150cf0af494fb9b3b98599afd91b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_10,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/650bc144529b4c23ab0198ffbea651cb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_11,color_FFFFFF,t_70,g_se,x_16)
1、抢占可以分为用户态抢占和内核态抢占。抢占时机?(中断、返回用户态、主动schedule())
2、Linux kernel是抢占式内核。
为什么要使用锁?-Why
为了性能,引入了多核。
但多核导致了并发,并发就导致了争抢,为了保证争抢有序,就出现了锁。
备注:
网上关于锁的介绍的文档很多,推荐知乎兰新宇的博客
术道经纬 - 知乎
关于锁的介绍,有一系列文章,个人感觉写的非常不错,我主要从这个博客学习锁的知识。
锁保护什么?-What
锁保护的对象:公共资源。公共资源,从某一方面讲,就是全局变量,或者从堆里面申请的共用内存。
如右图内核代码:
1、锁保护的是全局变量zone(从堆上申请的)。
2、无法保护局部变量tmp和low,局部变量也不需要保护,想想这是为什么?
3、代码段不需要保护,因为它是只读的。
误区:常认为锁保护的是一段代码和变量。
其实:从保护资源的角度,锁只是为了保护全局变量,公共资源是一种更严谨的通用的说法。若非要说“锁也保护了代码”,那也只是在保护全局变量的时候,顺便保护了代码而已。
使用锁,首先要搞清楚使用锁的目的是什么?要保护什么?
锁是如何保护资源的?--How
![](https://img-blog.csdnimg.cn/38a3f8200fc54222b86bd3a71aa17c6d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lya5YaZYnVn55qE56iL5bqP54y_X2dvb24=,size_18,color_FFFFFF,t_70,g_se,x_16)
核心原理:根据全局变量的状态来实现锁的控制。
“统一到门口排队,依次获得钥匙”
无进程持锁时,lock=0;
进程0持锁后,lock=1;
进程1再尝试持锁时,由于lock=1,无法获取,只能等待。
因此:lock一定要是全局的,8字节,其实只用1bit即可。Spinlock就充分利用这8个字节,仅用1bit表示锁状态。
衍生出锁的两大功能:同步和保护。
锁的种类
锁的分类 |
相关API |
特点 |
注意点 |
原子操作:atomic |
atomic_read(atomic_t * v); atomic_set
|