当前位置: 首页 > 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.dtcms.com/a/263961.html

相关文章:

  • 记录一次生产环境ActiveMQ无法启动的问题
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(八)
  • Raspberry Pi 4边缘智能PLC:OpenPLC赋能物联网
  • 25-7-1 论文学习(1)- Fractal Generative Models 何恺明大佬的论文
  • 半导体和PN结
  • 遥感影像岩性分类:基于CNN与CNN-EL集成学习的深度学习方法
  • 胖喵安初 (azi) Android 应用初始化库 (类似 Termux)
  • Adobe AI高效设计技巧与创新思维指南
  • day41简单CNN
  • 注意力得分矩阵求解例子
  • 网站崩溃的幕后黑手:GPTBot爬虫的流量冲击
  • 第七讲~~测试工具(禅道项目管理系统)
  • 【记录】Word|Word创建自动编号的多级列表标题样式
  • poi java 删除word的空白页
  • 【docker】docker save和docker load
  • 通达信【极弱强势指标与股道波段交易系统】幅图
  • Gin 中间件详解与实践
  • 发布/订阅模式:解耦系统的强大设计模式
  • Python Flask 容器化应用链路可观测
  • 基于SSM万华城市货运服务系统的设计与实现
  • 开源模型与商用模型协同开发机制设计
  • Vue基础(19)_Vue内置指令
  • Qt_Creator入门基础知识
  • 基于.Net的Web API 控制器及方法相关注解属性
  • Qt/C++运行报错:exited with code -1073741819
  • scp (Secure Copy Protocol)指令集
  • 向量数据库全面解析:原理、功能与主流产品对比
  • 学习笔记(C++篇)—— Day 9
  • Terraform Helm:微服务基础设施即代码
  • Kubernetes Pod 调度基础