嵌入式实践教程--【Linux驱动】Linux驱动开发基于Linux4.0+(二)——并发与同步

简介: 嵌入式实践教程--【Linux驱动】Linux驱动开发基于Linux4.0+(二)——并发与同步

一、概念



所谓并发,是指多个内核路径同时访问和操作数据,可能发生 覆盖共享数据的情况,造成被访问数据的不一致。


在内核中发生并发访问并发源主要有以下4种。


中断和异常


软中断和tasklet:软中断和tasklet可能随时会被调度运行,从而打断当前正在执行进程的上下文。


内核抢占:调度器支持内核抢占。


多处理器并发运行


上述情况需要针对单核和多核系统进行区别对待。


对于单处理器的系统有以下并发源:

1. 中断处理程序可以打断软中断、tasklet和进程上下文的执行

2. 软中断和tasklet并不会并发,但是可以打断进程上下文的执行

3. 在支持抢占的内核中,进程上下文会并发

4. 在不支持抢占的内核中,进程的上下文不会产生并发


对于SMP系统:

1. 同类型的中断并不会并发,但是不同类型的中断源可能会被送到不同的CPU上,因此可能会存在并发

2. 同类型的软中断会在不同的CPU上并发执行

3. 同类型的tasklet是串行执行,不会在多个CPU上并发

4. 不同CPU的进程上下文会并发

记住临界区的保护原则:是保护资源或者数据,而不是保护代码。(静态局部变量,全局变量,共享的数据结构,Buffer缓存,链表,红黑树等)


二、原子操作和内存屏障



1.ARM处理器中如何实现独占访问内存??


处理器中有Local monitor和Global monitor来实现ldrex和strex指令的独占访问,并且ldrex和strex保证的add操作的原子性。


i++用原子操作还是加锁的方式来保证它的原子性??


采用原子操作,加锁开销太大!


2.内存屏障


程序实际运行时内存访问顺序和程序代码编写的访问顺序不一致,会导致内存乱序访问.因此引入内存屏障以防止内存乱序访问.


数据存储屏障DMB(Data Memory Barrier)

数据同步屏障DSB(Data Sync Barrier)

指令同步屏障ISB(Instruction Sync Barrier)


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOvOiXLP-1635519975557)(https://i.imgur.com/W9oqnhp.jpg)]


内存屏障的使用场景举例:


在网卡驱动程序中发送数据包,网络数据包写入buffer后交给DMA引擎发送,wmb()保证在DMA传输前,数据被完全写入buffer中.


在内核里的睡眠和唤醒API也用到了内存屏障,在set_current_state()修改进程状态时插入内存屏障函数smp_wmb().唤醒时会调用wake_up(),在修改task状态之前也会隐式的插入smp_wmb()


3.自旋锁spinlock


1.spinlock的性质:

忙等待的所机制

同一时刻只能有一个代码路径获得该锁

锁持有者必须尽快完成临界区的任务


2.存在的问题:

在很多CPU争用同一个spinlock时,会导致严重的不公平性和性能下降。当该锁释放时,事实上可能刚刚释放该锁的CPU又会马上获得该锁的使用权,没有考虑那些已经在锁外面等待了很久的CPU。因为刚刚释放锁的CPU的L1 cache中存储了该锁,它比其他锁更快的获得自旋锁。


3spinlock锁实现的关键:

关闭内核抢占!!!

如果临界区允许内核抢占,那么如果临界区发生中断,中断返回时回去检查抢占调度。


因此就有两个问题:

①抢占调度相当于使得持有锁的进程睡眠,违背了spinlock不允许睡眠和快速执行的设计初衷;

②抢占调度进程也可能去申请获得spinlock锁,于是死锁就产生了。


4.使用spinlock的重要原则:

拥有spinlock锁的临界区必须是原子执行,不能休眠和主动调度。


5.spin_lock和raw_spin_lock的区别

在绝对不允许被抢占和睡眠的临界区,应该使用raw_spin_lock,否则使用spin_lock


4.信号量


信号量的可以同时允许任意数量的锁持有者,sema_init(struct *sem,int count),其中count大于1,可以允许多个持有者,计数信号量;count等于1,只允许一人持有锁,互斥信号量。信号量允许睡眠。可以用于并行处理环境。


5.Mutex互斥体


Linux内核已经有了信号量机制,为何还要单独设置一个Mutex机制呢??


信号量相当于多个厕所;Mutex相当于一个厕所,一次只允许一个人进去。Mutex比信号量执行速度快,可扩展性更好,Mutex数据结构的定义比信号量小。


Mutex实现了自旋等待的机制,更准确的说,他比读写信号量更早的实现了自旋等待机制。在实现自旋等待机制时,内核实现了一套MCS锁机制(一种自旋锁优化方案)来保证只有一个人自旋等待持有者释放锁。


MCS避免多个CPU争用锁导致CPU高速缓存行颠簸现象


1.Mutex锁的实现

Mutex锁的初始化有两种方式:

静态使用DEFINE_MUTEX宏

动态使用mutex_init()函数


小结


Mutex使用场景:


  1. 同一时刻只有一个线程可以持有Mutex
  2. 只有锁持有者可以解锁.
  3. 不允许递归加锁和解锁
  4. 进程持有Mutex不能退出
  5. 必须使用官方API来初始化
  6. 可以睡眠,但是不允许在中断处理程序或中断下半部使用.


在实际工程中,如何使用spinlock和Mutex???


中断上下文,毫不犹豫地使用spinlock,临界区含有睡眠,隐含睡眠的动作及内核API,避免使用spinlock.信号量和Mutex,优先使用Mutex.

相关文章
|
6天前
|
存储 IDE Linux
零基础保姆级教程!手把手教你免费玩转Linux CentOS安装+学习环境搭建(附避坑指南)
本文详细介绍了在VMware虚拟机中安装CentOS 6.8的全过程。首先,需确保已安装VMware并开启V-CPU虚拟化功能,可通过BIOS设置或使用LeoMoon CPU-V工具检测。接着,下载CentOS镜像文件,并在VMware中新建虚拟机,配置CPU、内存、硬盘等参数。最后,加载ISO镜像启动虚拟机,按照提示完成CentOS的安装,包括语言、键盘、存储方式、地区、密码设置及硬盘分区等步骤。安装完成后,以root用户登录即可进入系统桌面,开始学习Linux命令和操作。
51 12
零基础保姆级教程!手把手教你免费玩转Linux CentOS安装+学习环境搭建(附避坑指南)
|
1月前
|
Shell Linux
【linux】Shell脚本中basename和dirname的详细用法教程
本文详细介绍了Linux Shell脚本中 `basename`和 `dirname`命令的用法,包括去除路径信息、去除后缀、批量处理文件名和路径等。同时,通过文件备份和日志文件分离的实践应用,展示了这两个命令在实际脚本中的应用场景。希望本文能帮助您更好地理解和应用 `basename`和 `dirname`命令,提高Shell脚本编写的效率和灵活性。
96 32
|
1月前
|
Unix Linux C语言
【Linux】 Linux makefile 教程
本文详细介绍了 Linux 环境下 Makefile 的基本结构、语法和使用方法,并通过一个简单的 C++ 项目示例演示了 Makefile 的实际应用。Makefile 是一个强大而灵活的工具,通过合理配置,可以极大地简化项目的编译和管理过程,提高开发效率。希望本文能帮助您更好地理解和应用 Makefile,在实际项目中高效管理代码的编译和构建。
55 20
|
1月前
|
Unix Linux C语言
【Linux】 Linux makefile 教程
本文详细介绍了 Linux 环境下 Makefile 的基本结构、语法和使用方法,并通过一个简单的 C++ 项目示例演示了 Makefile 的实际应用。Makefile 是一个强大而灵活的工具,通过合理配置,可以极大地简化项目的编译和管理过程,提高开发效率。希望本文能帮助您更好地理解和应用 Makefile,在实际项目中高效管理代码的编译和构建。
62 16
|
2月前
|
Ubuntu Linux 网络安全
Linux磁盘挂接教程
Linux磁盘挂接教程
76 14
|
3月前
|
消息中间件 Java Kafka
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
本文介绍了Kafka集群的搭建过程,涵盖从虚拟机安装到集群测试的详细步骤。首先规划了集群架构,包括三台Kafka Broker节点,并说明了分布式环境下的服务进程配置。接着,通过VMware导入模板机并克隆出三台虚拟机(kafka-broker1、kafka-broker2、kafka-broker3),分别设置IP地址和主机名。随后,依次安装JDK、ZooKeeper和Kafka,并配置相应的环境变量与启动脚本,确保各组件能正常运行。最后,通过编写启停脚本简化集群的操作流程,并对集群进行测试,验证其功能完整性。整个过程强调了自动化脚本的应用,提高了部署效率。
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
|
4月前
|
Linux Python
Linux 中某个目录中的文件数如何查看?这篇教程分分钟教会你!
在 Linux 系统中,了解目录下文件数量是常见的需求。本文介绍了四种方法:使用 `ls` 和 `wc` 组合、`find` 命令、`tree` 命令以及编程实现(如 Python)。每种方法都附有详细说明和示例,适合不同水平的用户学习和使用。掌握这些技巧,可以有效提升系统管理和日常使用的效率。
2280 6
|
4月前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
73 5
|
3月前
|
Ubuntu Linux C++
Win10系统上直接使用linux子系统教程(仅需五步!超简单,快速上手)
本文介绍了如何在Windows 10上安装并使用Linux子系统。首先,通过应用商店安装Windows Terminal和Linux系统(如Ubuntu)。接着,在控制面板中启用“适用于Linux的Windows子系统”并重启电脑。最后,在Windows Terminal中选择安装的Linux系统即可开始使用。文中还提供了注意事项和进一步配置的链接。
105 0
|
4月前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
103 6