当前位置: 首页 > news >正文

Linux驱动学习day13(同步与互斥)

内联汇编

需要使用特殊指令(ldrex/strex实现互斥访问)的时候需要使用内联汇编。下图是内联汇编的语法。

__asm__  冒号之前的使用" "括起来的是汇编代码,下面这个例子只有一条汇编指令。下面这段代码的意思是把第1个操作数和第2个操作数累加起来放到第0个操作数。最后一行代表除了上面那些寄存器之外还影响着什么寄存器。cc代表flags register。“r”(a)表示将a的值放入r这个寄存器中。“=r”(sum)表示这个寄存器的值会被放在sum这个变量中。

还有一种写法:“=&r”(a) earlyclobber:表示这个输出寄存器 在所有输入操作数还没加载完成之前就会被修改,在分配寄存器时,a用的寄存器从一开始就不能与任何输入寄存器重复防止GCC 把这个输出寄存器和某个输入变量放在同一个寄存器中。这是在涉及读-改-写(如 ldrex/strex)时非常关键的一个约束

#include <stdio.h>
#include <stdlib.h>int add(int a, int b)
{int sum;__asm__ volatile ("add %0, %1, %2\n""add %1, %1, #1\n""add %2, %2, #1\n":"=&r"(sum):"r"(a), "r"(b):"cc");return sum;
}int main(int argc, char **argv)
{int a;int b;if (argc != 3){printf("Usage: %s <val1> <val2>\n", argv[0]);return -1;}a = (int)strtol(argv[1], NULL, 0);	b = (int)strtol(argv[2], NULL, 0);	printf("%d + %d = %d\n", a, b, add(a, b));return 0;
}

 当上述代码:"=&r"(sum) 写为:"=r"(sum)的时候的结果1+2 = 4 , 这是因为由于 sum 和 a 用了同一个寄存器(x1),第二句实际是对 sum 又加了一次,所以 1 + 2 → 3 → +1 = 4

从汇编语言也可以看出x0 = x0 + x1  第二步 x0 = x0 + 1 第三步x1 = x1 + 1 。这就说明了为什么实现的结果是1+2 = 4.(这种情况就是操作数0和1在同一个寄存器)

 

将 代码写为"=&r"(sum)再次运行

 

 同步互斥失败案例

第一种情况比较简单,用一个全局变量来控制访问,这种情况在修改全局变量前中断来到,另一个进程再调用驱动就会导致失败。

第二种情况如下图所示,用一个变量直接控制是否访问,但是--valid这个可以拆分成先读出数据,再修改,最后写入数据,如果刚读出还没有修改的时候被抢占,变量还未修改,另外一个线程进程也能访问。

第三种情况如下图所示代码,在单核CPU可以实现锁,但是在多核CPU无法实现,一个锁刚关住一个CPU的中断,还未改变值,另外一个CPU也可以使用该驱动。 

使用原子变量实现互斥

在ARMv6以下的版本,这些原子操作的函数都会进行关中断和恢复中断,确保没有别的程序能够访问,但是在ARMv6及以上版本,使用的是如下方法:

 当asm_op,op为add的时候分析下面代码:3568内核中的文件和6ull板子上的不同。

#define ATOMIC_OP(op, asm_op)						\
static inline void atomic_##op(int i, atomic_t *v)			\
{									\register int w0 asm ("w0") = i;					\register atomic_t *x1 asm ("x1") = v;				\\asm volatile(							\__LSE_PREAMBLE							\ARM64_LSE_ATOMIC_INSN(__LL_SC_ATOMIC(op),			\
"	" #asm_op "	%w[i], %[v]\n")					\: [i] "+r" (w0), [v] "+Q" (v->counter)				\: "r" (x1)							\: __LL_SC_CLOBBERS);						\
}

 借助GPT展开上面那段代码:

static inline void atomic_add(int i, atomic_t *v)
{register int w0 asm ("w0") = i;register atomic_t *x1 asm ("x1") = v;asm volatile (__LSE_PREAMBLE
#ifdef USE_LSE"ldadd %w[i], %w[i], [%[v]]\n"
#else"1: ldxr %w[i], [%[v]]\n""   add %w[i], %w[i], %w0\n""   stxr w3, %w[i], [%[v]]\n""   cbnz w3, 1b\n"
#endif: [i] "+r"(w0), [v] "+Q"(v->counter): "r"(x1): "memory", "cc");
}

代码解释如下: 

 这样来看,在ARMv6及以上版本(多CPU),函数其实是有可能会被打断的,但是其实现了原子操作的效果,(但是打断了没有关系,重新执行上述代码,如下图,重新执行之后发现valid = 0 , 走if之外的分支)。

常用函数: atomic_inc_and_test():先加1,再判断新值是否等于0,等于0的话返回1;

atomic_del_and_test():先减1,再判断新值是否等于0,等于0的话返回1。

http://www.lqws.cn/news/601309.html

相关文章:

  • AI的未来:人类会被取代,还是变得更强大?
  • HTTPS安全传输时采用的顶级阳谋
  • 【Maven 】 <resources> 配置中排除 fonts/** 目录无效,可能是由于以下原因及解决方案:
  • 编写rosbag脚本记录雷达与imu数据包
  • 学习经验分享【41】YOLOv13:基于超图增强自适应视觉感知的实时目标检测
  • day41简单CNN
  • 【Web3.0】Web1.0、Web2.0和Web3.0有哪些区别联系?
  • MySQL 8.4 备份与恢复完全指南
  • win11,visual studio 2022,配置dcmtk,opencv
  • 【记录】Word|Word创建自动编号的多级列表标题样式
  • 结构体实战:用Rust编写矩形面积计算器
  • Linux安装JDK和Maven
  • vue中scss下载方式与引入方式
  • 【深度学习1】ModernBert学习
  • 【IP 潮玩行业深度研究与学习】
  • SpringCloud系列(45)--SpringCloud Bus简介
  • 基于Spring Cloud微服务架构的API网关方案对比分析
  • 快应用(QuickApp)技术解析与UniApp跨端开发生态探秘优雅草卓伊凡
  • 振荡电路Multisim电路仿真实验汇总——硬件工程师笔记
  • 在CPU设计中,为什么要引入指令集架构?有什么好处?-- 数字IC笔试
  • 强化学习:Policy Gradients 学习笔记
  • 1.MySQL之如何定位慢查询
  • AI赋能智慧餐饮:Spring Boot+大模型实战指南
  • js严格模式和非严格模式
  • 从docker-compose快速入门Docker
  • JVM 中的垃圾回收算法及垃圾回收器详解
  • JavaWeb笔记02
  • 渗透测试(Penetration Testing)入门:如何发现服务器漏洞
  • pcap流量包分析工具设计
  • 数据结构:递归:斐波那契数列(Fibonacci Sequence)