OK6410A 开发板 (八) 66 linux-5.11 OK6410A linux 并发 竞态 与 同步

并发执行单元引起的错误 : 竞态案例
a 初始化 为 0
A 内核进程 对 变量 a 加1
B 内核进程 对 变量 a 加1
a 现在 为 2

下面的顺序不会有问题
A load 	内存中的a 进 寄存器
A 寄存器 	自加 1
A store 	寄存器中的值 进 内存
B load 	内存中的a 进 寄存器
B 寄存器 	自加 1
B store 	寄存器中的值 进 内存
此时a的值为2


下面的顺序会有问题
A load 	内存中的a 进 寄存器1
A 寄存器1 	自加 1
B load 	内存中的a 进 寄存器2
B 寄存器2 	自加 1
A store 	寄存器1中的值 进 内存
B store 	寄存器2中的值 进 内存
此时a的值为1 // 此时发生了竞态 : 意想不到的事件

// 此时 a 被 两个内核线程访问,a叫做共享资源
// 此时 "A 内核进程 对 变量 a 加1" 这个动作相关的代码叫做临界区


内核态并发原因分类
-----------------------------------------竞态

由于 中断 调度 	的存在, 会存在 伪并发
由于 SMP 		的存在, 会存在 真并发

解决 中断 矛盾问题引入了软中断,也就引入了 软中断 产生的竞态
在 __irq_svc 返回时调度, 也就引入了内核抢占的 竞态

总之, 竞态原因有以下五种
	中断
	调度
	SMP
	软中断
	内核抢占

对于内核代码(驱动,内核线程,中断,其他内核模块)来说(因为地址空间相同),存在的并发原因有五种
	中断
	调度
	SMP
	软中断
	内核抢占
  • 非SMP的内核态并发路径
中断处理程序可以打断软中断,tasklet和进程上下文的执行
软中断和tasklet之间不会并发,但可以打断进程上下文的执行
在支持抢占的内核中,进程上下文之间会产生并发
在不支持抢占的内核中,进程上下文之间不会产生并发

  • SMP的内核态并发路径
同一类型的中断处理程序不会并发,但是不同类型的中断有可能被送到不同的cpu上,因此不同类型的中断处理程序可能存在并发执行
同一类型的软中断会在不同的cpu上并发执行
同一类型的tasklet是串行执行的,不会再多个cpu上并发
不同cpu上的进程上下文会并发
  • 内核态哪些资源需要保护
静态局部变量
全局变量
共享的数据结构
缓存
链表
红黑树
用户态并发原因分类
对于应用代码来说,存在的并发原因有一种(对 进程地址空间中的内存相同部分 进行并发访问),
这时候我们不对 并发原因(并发原因只有调度)分类,而是对(共享资源)分类,共享资源分为多种
	多线程代码中的所有变量
	多进程代码中的共享内存中的变量
解决竞态的方案
  • 同步的概念
解决竞态的方案 叫做 同步
所有的 同步 都是基于原子操作,是芯片提供的指令,不是软件做的.软件做的是在原子操作的封装
同步根据 竞态原因 的不同, 而 分为多种

  • 所有同步机制都存在的问题 - 乱序
// 涉及同步时,指令重排可能会带来问题,如果放在同步原语之后的指令在同步原语之前被执行了,就可能会出问题
我们可以用不同的 同步 方法 围住 共享资源 保证 在 某个情景下 不会产生竞态
虽然在代码上,我们的同步方法围住了共享资源, 但是 却 不能保证 共享资源 一定会被 同步 方法 围住.因为存在着cpu重排序指令这个问题
这个问题主要分为以下几种: // https://blog.csdn.net/Roland_Sun/article/details/106895899
	1.编译器编译时的优化
	2.处理器执行时的多发射和乱序优化
	3.读取和存储指令的优化
	4.缓存同步顺序(导致可见性问题)

而cpu 重排序指令的 解决方案 为 内存屏障原语,在外表现为多种形式
	1. volatile 关键字 // 被volatile修饰的变量在编译成字节码文件时会多个lock指令,该指令在执行过程中会生成相应的内存屏障,以此来解决可见性跟重排序的问题
	2. barrier() // 解决编译器乱序
	3. mb/rmb/wmb/smp_mb/smp_rmb/smp_wmb // 解决运行时乱序



volatile 关键字使用的是Lock指令,volatile的作用取决于Lock指令。 
	// ??? TODO volatile  的实现 
	// volatile 与 屏障 的区别
	Lock是软件指令
	Lock前缀,Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁,可以理解为CPU指令级的一种锁。类似于Lock指令。
	volatile的变量在进行写操作时,会在前面加上lock质量前缀。
	Lock前缀,Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁,可以理解为CPU指令级的一种锁。
	而Lock前缀是这样实现的
		它先对总线/缓存加锁,然后执行后面的指令,最后释放锁后会把高速缓存中的脏数据全部刷新回主内存。
		在Lock锁住总线的时候,其他CPU的读写请求都会被阻塞,直到锁释放。Lock后的写操作会让其他CPU相关的cache失效,从而从新从内存加载最新的数据,这个是通过缓存一致性协议做的。 

	lock前缀指令相当于一个内存屏障(也称内存栅栏)(既不是Lock中使用了内存屏障,也不是内存屏障使用了Lock指令),内存屏障主要提供3个功能:
		确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
		强制将对缓存的修改操作立即写入主存,利用缓存一致性机制,并且缓存一致性机制会阻止同时修改由两个以上CPU缓存的内存区域数据;
		如果是写操作,它会导致其他CPU中对应的缓存行无效。

内存屏障是CPU指令

  • 所有同步机制的基础 - 同步原语(Synchronization primitives)
swap and swap byte instructions  在ARMv6中不推荐使用,建议所有软件都迁移到使用新的同步原语
ARMv6提供了一种新的机制来支持更全面的非阻塞共享内存同步原语,这种原语可以扩展到多处理器系统设计中
	Load-Exclusive 	: LDREX
	Store-Exclusive : STREX
  • 不同的同步机制

不同的同步机制 都是 以 同步原语(Synchronization primitives) 和 屏障 为基础的

针对不同的 竞态原因,有不同的同步方法
	//	也就是说,如果你要怕因为中断产生竞态,那么就关中断
	//  但是该技术(关中断)不会解决 其他竞态原因 产生的竞态
	per-cpu 变量
	atomic bit 64位 变量
	禁中断/中断屏蔽
	禁抢占
	禁软中断
	自旋锁
	读写锁
	顺序锁
	信号量(count初始化为1)/读写信号量
	互斥锁
	RCU
	大内核锁BLK
其他易混淆的概念
  • 解决竞态的方案(同步)与事件同步的区别
解决竞态的方案(同步) 是 为了 不让 并发(真并发和伪并发),保证数据的一致性

而 事件同步 是为了 两个事件必须有先后顺序(叫做两个同步事件)
不相干的事件 不需要 有先后顺序

而 不管 两个事件(A B)要不要先后顺序,都需要 做 解决竞态的方案(同步) // 并发可能是 A与C B与C 的并发


设计 让 两个事件同步 的 机制 有
	1. 等待一段时间			// sleep
	2. 等待事件完成或条件满足 // 信号量(count初始化为0) 和 等待队列 和 completion

  • 事件的同步和事件的异步

// 其实 异步事件 的使用场景 和 同步事件 的 使用场景 没有任何关系 ,没有可比性
// 这里 只是 其分类为 同步和异步

事件同步,某事件等待另一件事件结束
事件异步,某事件不等待另一事件结束


A 等待 B 结束
	1. A 等待一段时间(sleep并调出) 等到 硬件复位(B)							// sleep
	2. A 调用 poll(wait并调出) 等待事件完成或条件满足(数据到来)(B)   			// 等待队列

A 不等待 B 结束
	1. A 调用 提交任务给 tasklet/工作队列 ,A 继续执行 . B在一个时机处理 任务	// tasklet/软中断/工作队列
	2. A 调用 设置回调 给 timer/hrtimer  ,A 继续执行 . 定时器到了,B调用回调 	// 定时器timer/定时器hrtimer

  • 异常的 同步 和 异步
同步 : 确定会会断在哪条指令上
异步 : 不确定会会断在哪条指令上
  • IO 的同步和异步
进程A的一个IO请求分为两个过程
	1.等待数据准备就绪
	2.将数据从内核态拷贝到用户空间

阻塞IO与非阻塞IO的定义 
	第一个过程阻塞/非阻塞(A)对应 阻塞IO/非阻塞IO(B)

同步IO与异步IO的定义
	第二个过程阻塞/非阻塞(A)对应 同步IO/异步IO(非同步)


read 非阻塞
	第一个过程 阻塞
	第二个过程 阻塞
	第一个过程使用的 api read 不阻塞(此时说的是read的等待数据准备好)
	第二个过程使用的 api read 阻塞(此时说的是read的拷贝数据)

read 阻塞
	第一个过程 阻塞
	第二个过程 阻塞
	第一个过程使用的 api read 阻塞(此时说的是read的等待数据准备好)
	第二个过程使用的 api read 阻塞(此时说的是read的拷贝数据)


select
	第一个过程 阻塞
	第二个过程 阻塞

	第一个过程使用的 api select 阻塞(此时说的是select的等待数据准备好)
	第二个过程使用的 api read 	阻塞(此时说的是read的拷贝数据,不是read的等待数据准备好)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值