当一个线程正在编写另一线程可能同时执行的代码时,如何在 ARM 上同步?

2024-03-07

考虑一个多核 ARM 处理器。一个线程正在修改可能由另一线程同时执行的机器代码块。修改线程执行以下类型的更改:

  1. 将机器代码块标记为跳过:它将跳转指令放置为代码块的第一条指令,这样无论谁执行它都应该跳过其余指令,跳过整个代码块。
  2. 标记要执行的机器代码块:它从第二条指令开始写入其余指令,然后用代码块的预期第一条指令自动替换第一条指令(跳转)。

对于代码编写器线程,我知道用以下命令进行最终编写就足够了std::memory_order_release在 C++11 中。

然而,尚不清楚执行器线程端要做什么(它失控,我们只控制我们编写的机器代码块)。我们是否应该在被修改的代码块的第一条指令之前写一些指令屏障?


我认为您的更新过程不安全。与 x86 不同,ARM 的指令缓存与数据缓存不一致,根据此自修改代码博客文章 https://community.arm.com/groups/processors/blog/2010/02/17/caches-and-self-modifying-code.

非跳转第一指令仍然可以被缓存,因此另一个线程可以进入该块。当执行到达块的第二个 i-cache 行时,可能会重新加载该行并看到部分修改的状态。

还有另一个问题:中断(或上下文切换)可能会导致仍在执行旧版本的线程中驱逐/重新加载缓存行。就地重写指令块要求您确保在修改内容后所有其他线程中的执行都已退出该指令块,以便新线程不会进入该指令块。即使对于一致的 I-cache(如 x86),并且即使代码块适合单个缓存行,这也是一个问题。

我认为没有任何方法可以使 ARM 上的就地重写同时安全且高效。

如果没有一致的 I 缓存,您也无法保证其他线程能够通过此设计及时看到代码更改,而无需在每次运行之前从 L1I 缓存中刷新块等极其昂贵的操作。

使用一致的 I-cache(x86 风格),您只需等待足够长的时间,以防止另一个线程完成旧版本的执行时出现任何可能的延迟。即使该块不执行任何 I/O 或系统调用,缓存未命中和上下文切换也是可能的。如果它以实时优先级运行,特别是在禁用中断的情况下,那么最坏的缓存只是缓存未命中,即不是很长。否则我不会打赌任何少于一两个时间片(也许 10 毫秒)的东西都是真正安全的。


这些幻灯片很好地概述了 ARM 缓存,主要关注 ARMv8 http://events.linuxfoundation.org/sites/events/files/slides/slides_17.pdf.

我实际上要引用另一张幻灯片(关于虚拟化 ARM) https://events.linuxfoundation.org/sites/events/files/slides/slides_10.pdf#page=8对于这个要点摘要,但我建议阅读 ELC2016 幻灯片,而不是虚拟化幻灯片。

在某些情况下,软件需要了解缓存:可执行代码加载/生成

  • 需要 D 缓存清理以实现统一点 + I 缓存失效
  • 可能来自 ARMv8 上的用户空间
  • 需要在 ARMv7 上进行系统调用

D-cache 可以在有或没有写回的情况下失效(因此请确保清理/刷新而不是丢弃!)。您可以而且应该通过虚拟地址触发此操作(而不是立即刷新整个缓存,并且绝对不要为此使用按设置/方式刷新的内容)。

如果您在使 I-cache 失效之前没有清理 D-cache,则在 L2 丢失后,code-fetch 可以直接从主内存获取到非连贯 I-cache。 (无需在任何统一缓存中分配陈旧行,这MESI https://en.wikipedia.org/wiki/MESI_protocol会阻止,因为 L1D 的线路处于修改状态)。无论如何,从架构上来说,将 L1D 清理到 PoU 是必需的,并且无论如何都发生在非性能关键的编写器线程中,因此最好只是这样做,而不是试图推理对于特定的 ARM 微体系结构不这样做是否安全。请参阅 @Notlikethat 的评论,努力消除我对此的困惑。

有关从用户空间清除 I-cache 的更多信息,请参阅如何在 Linux 2.6.35 上从用户模式清除和无效 ARM v7 处理器缓存 https://stackoverflow.com/questions/6046716/how-clear-and-invalidate-arm-v7-processor-cache-from-user-mode-on-linux-2-6-35。海湾合作委员会的__clear_cache()功能和Linuxsys_cacheflush只适用于曾经的内存区域mmapPROT_EXEC.


不要就地修改:使用新位置

在您计划拥有整个检测代码块的地方,放置一个间接跳转(或保存/恢复lr和一个函数调用(如果你无论如何都要有一个分支)。每个块都有自己的跳转目标变量,可以自动更新。这里的关键是间接跳转的目标是data,所以它是一致的与来自写作线程的商店。

由于您以原子方式更新指针,因此使用者线程要么跳转到旧的代码块,要么跳转到新的代码块。

现在您的问题是确保没有核心在其 i-cache 中拥有新位置的陈旧副本。考虑到上下文切换的可能性,如果上下文切换没有完全刷新 i-cache,则包括当前的核心。

如果您为新块使用足够大的位置环形缓冲区,以便它们在足够长的时间内未使用而被驱逐,那么在实践中可能永远不会出现问题。不过,这听起来非常难以证明。

如果与其他线程运行这些动态修改块的频率相比更新频率较低,那么它可能足够便宜让发布线程触发其他线程中的缓存刷新写入新块后,但是before更新间接跳转指针以指向它。


强制其他线程刷新其缓存:

Linux 4.3 及更高版本有membarrier()系统调用 http://man7.org/linux/man-pages/man2/membarrier.2.html在返回之前,它将在系统中的所有其他核心上运行内存屏障(通常带有处理器间中断)(从而屏障所有进程的所有线程)。也可以看看这篇博文 https://www.msully.net/blog/2015/02/24/forcing-memory-barriers-on-other-cpus-with-mprotect2/描述一些用例(例如用户空间 RCU)以及mprotect()作为备选。

不过,它似乎不支持刷新指令缓存。如果您正在构建自定义内核,您可以考虑添加对新内核的支持cmd or flag值意味着刷新指令缓存而不是(或同时)运行内存屏障。也许是flag值可以是虚拟地址吗?这仅适用于地址适合的架构int,除非您调整系统调用 API 以查看完整的寄存器宽度flag对于你的新命令,但只有int对现有的价值MEMBARRIER_CMD_SHARED.


除了破解 membarrier() 之外,您还可以向消费者线程发送信号,并让它们的信号处理程序刷新 i-cache 的适当区域。这是异步的,因此生产者线程不知道何时可以安全地重用旧块。

IDK if munmap()它会起作用,但它可能比必要的更昂贵(因为它必须修改页表并使相关的 TLB 条目无效)。


其他策略

您也许可以通过在共享变量中发布单调递增的序列号来执行某些操作(具有释放语义,因此它是按指令写入顺序排列的)。然后,消费者线程根据线程本地最高可见值检查序列号,如果有新内容,则使 i-cache 无效。这可以是每个块的或全局的。

这并不能直接解决检测运行旧块的最后一个线程何时离开它的问题,除非那些每个线程最高可见的计数器实际上不是线程本地的:仍然是每个线程,但生产者线程可以查看他们。它可以扫描它们以查找任何线程中的最低序列号,如果该序列号高于块未被引用时的序列号,则现在可以重用该块。小心虚假分享 https://software.intel.com/en-us/articles/avoiding-and-identifying-false-sharing-among-threads/:不要使用全局数组unsigned long对于它,因为您希望每个线程的私有变量与其他线程本地内容位于单独的缓存行中。


另一种可能的技术:如果只有一个消费者线程,则生产者将跳转目标指针设置为指向不会更改的块(因此不需要刷新 i-cache)。该块(在消费者线程中运行)对 i-cache 的相应行执行高速缓存刷新,然后再次修改跳转目标指针,这次指向应该每次运行的块。

对于多个消费者线程,这会变得有点笨拙:也许每个消费者都有自己的私有跳转目标指针,而生产者会更新所有这些指针?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当一个线程正在编写另一线程可能同时执行的代码时,如何在 ARM 上同步? 的相关文章

  • ASP.NET MVC:DropDownListFor 未选择任何选项

    我用它来填充 ASP NET MVC 视图中的下拉列表 调试这个我可以看到Selected属性设置为true当它应该是的时候 但是当渲染视图时 列表中的任何选项都不
  • iTextSharp 居中对齐文档对象内的对象

    是否有一种快速而简单的方法可以将文档对象中的对象居中对齐 Without执行任何计算逻辑 即 获取页面宽度 获取内容宽度 除以二等 我在 Document 对象内的 Paragraph 对象中有一个 PdfPTable 对象 我想将段落对象
  • ASP.NET 中的网络凭据错误

    我正在尝试使用 NetworkCredential 类通过 ASP NET 访问网页 但是我不断收到以下消息的异常System Security Cryptography CryptographicException The handle
  • 设置 DataContract 和 DataMember 而不包含所有属性

    我找到了 DataContract and DataMember 属性有点混乱 宁愿使用配置方法或其他内容中的代码来完成此操作 这可能吗 您根本不必使用这些属性 DataContractSerializer将使用 getter 和 sett
  • 无法从 GetProcessId(.. hWnd) (pInvoke) 中提取 processID

    我使用以下方法 DllImport kernel32 dll SetLastError true static extern int GetProcessId IntPtr hWnd 尝试获取正在运行的进程的 processId 我拥有的唯
  • 并发 log4j

    我有自己的日志引擎 它将日志写入带有阻塞队列的单独线程上 为了使用 标准软件 我正在考虑切换到 log4j 我不希望我的高并发软件因日志命令而变慢 这些日志命令在调用命令时将所有内容写入磁盘 log4j 可以用作垃圾箱吗 Log4j 是大多
  • 迭代 C++ 映射中的键

    有没有办法迭代键 而不是 C 映射对 地图是关联容器 因此 迭代器是一对key val 如果您只需要键 则可以忽略该对中的值部分 for std map
  • 如果 POSIX 系统上不存在目录,是否有办法自动创建该目录?

    POSIX 系统上是否有任何方法可以仅在目录尚不存在时才自动创建该目录 如同 int fd open path to file O CREAT O EXCL O RDWR 0644 这不起作用 int dfd open path to di
  • csharp类可以像java类一样“继承”xml文档吗?

    我正在向一些csharp代码添加注释 并且我正在使用 net 或其他东西 提供的xml语言 我有一个接口和一些实现类 我在界面中有一个方法 它有一个注释 在实现类中没有对实现方法进行注释 当人们在java中这样做时 javadoc在生成文档
  • pthread_join() 中的阻塞

    根据手册页 pthread join 函数应暂停调用的执行 线程直到目标线程终止 除非目标线程 已经终止了 因此 据我了解 调用进程将阻塞 直到指定的线程退出 现在考虑以下代码 pthread t thrs NUMTHREADS for i
  • CTAD 可以在模板类的成员内部使用吗?

    C 有一个有用的功能 即模板参数隐含在模板类内的代码中A 然而 对于建筑来说 这似乎与 CTAD 发生冲突 如何让 CTAD 优先 例如 这里有一个错误f会员因为A被解释为A
  • 字符串初始化的 gcc 诊断不一致

    我正在使用 gcc 4 9 1 Mingw 并使用以下命令编译代码 gcc test c otest exe std c11 迂腐错误 Wall Wextra 此代码给出诊断 int main void char a 5 h e l l o
  • ASP.NET C# 捕获类中的所有异常

    我知道这不是正确的做法 而且根本不干净 我只是想知道这是否可能 如果我有一个包含很多方法的类 public class Foo methodA methodB methodC 是否可以捕获所有可能发生的异常 而不必在每个方法中编写 try
  • C++ GetDIBits 不工作

    首先我加载图像 cool bmp 加载很好 然后我调用函数 getPixArray 但它失败了 case WM CREATE runs once on creation of window hBitmap HBITMAP LoadImage
  • 汇编中如何计算负数

    我是汇编新手 我有一个关于如何表示负数的问题 我有三个 DWORDS 变量 比方说 result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式 结果 i j 8 但是 当我执行 i j 时 由于符号 结果将是一个
  • HTTP 请求未经客户端身份验证方案“Ntlm”的授权 从服务器收到的身份验证标头为“NTLM”

    我知道有很多与此类似的问题 但我找不到针对这一特定问题的问题 首先有几点 I have 无控制通过我们的 Sharepoint 服务器 我无法调整任何 IIS 设置 我相信我们的IIS服务器版本是IIS 7 0 我们的 Sharepoint
  • Oracle 数据库,SQL 更新语句将不起作用 (OLEDB)

    我设置了一个数字主键和一个存储卡车 FINS 的字母数字字段 它只是数字和字母的随机组合 我不生成鳍片 这些鳍片将始终与卡车车队识别号相同 这是代码视图 storeTruckSplit truckSplit 1 Stores truck F
  • 布局兼容类型的联合

    看这段代码 struct A short s int i struct B short s int i union U A a B b int fn U u u a i 1 return u b i 是否保证fn 回报1 注意 这是一个后续
  • OpenGL 和加载/读取 AoSoA(混合 SoA)格式的数据

    假设我有以下 AoSoA 格式的简化结构来表示顶点或点 struct VertexData float px 4 position x float py 4 position y 也就是说 每个实例VertexData存储4个顶点 我见过的
  • 加速Cuda程序

    要更改哪一部分来加速此代码 代码到底在做什么 global void mat Matrix a Matrix b int tempData new int 2 tempData 0 threadIdx x tempData 1 blockI

随机推荐

  • 接口和抽象类中的 Xml 属性

    今天发现了一件让我很困惑的事情 1 如果我有这个 public interface INamed XmlAttribute string Name get set public class Named INamed public strin
  • 人名中允许使用哪些字符? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Git / 分离 HEAD,恢复工作吗?

    我对我认为是我的分支进行了数十次提交 然后检查了另一个分支 愿意回到我最初的分支 我没有找到我更新的代码 在控制台中查看我的历史记录后 我了解到我在一个独立的分支工作 是否有可能获得我在独立分支上完成的工作 是的 您可以使用重新记录 尝试g
  • date() 方法,“遇到格式不正确的数值”不希望格式化 $_POST 中传递的日期

    不幸的是我不能使用DateTime 因为该项目所在的服务器正在运行 PHP v 5 2 有问题的行 aptnDate2 date Y m d POST nextAppointmentDate 抛出以下错误 Notice A non well
  • 如何同步线上线下数据库

    我有一个Web应用程序为我的客户提供一些信息 我有另一个版本 windows that exactly work same as web application 这是因为 Web 连接可能会丢失几个小时 而此时用户将使用该应用程序 我想知道
  • Jenkins:在升级构建中使用存档的工件

    我已经将一个工件归档为构建的最后一步 它可以如下所示使用 https xxx ci cloudbees com job xxx 52 artifact target xxx 1 2 1 SNAPSHOT r8304 20130807 150
  • Unix shell 命令的一般语法是什么?

    特别是 为什么有时某些命令的选项前面有一个 标志 有时由 sign 例如 sort f sort nr sort 4n sort 3nr 如今 POSIX 标准使用getopt http pubs opengroup org onlinep
  • 何时使用 StringIO,而不是连接字符串列表?

    使用 StringIO 作为字符串缓冲区比使用列表作为缓冲区慢 什么时候使用StringIO from io import StringIO def meth1 string a for i in range 100 a append st
  • 在 Swift 中将变量传递回父级

    我正在重写一个将代码从 Objective C 转换为 swift 的教程 该应用程序从 VC 开始 其中有 3 个滑块 红色 绿色和蓝色 用于设置背景颜色 颜色名称标签和链接到第二个 VC 的按钮 在第二个 VC 中 第一个 VC 中的颜
  • Nestjs 服务级别缓存

    查看 Netsjs 文档 我可以看到一般方法是利用 CacheInterceptor 进行控制器级缓存 我希望实现的是服务 数据库级缓存 用例主要用于其他服务所需的静态数据库数据 是否有办法扩展提供的缓存模块以在服务内使用 我也在寻找一种方
  • java.lang.UnsatisfiedLinkError:无法加载 stlport_shared:findLibrary 返回 null(tess-two)

    我正在使用 sqlcipher jar 在 android 中加密数据库 并在中使用它的本机库 libs armeabi 文件夹 1 lib数据库sqlcipher so 2 libsqlcipher android so 3 libstl
  • 如果标题是从不参与选项菜单的 Fragment 设置的,则 Activity 的标题区域不会展开

    这与提到的问题非常相似here https stackoverflow com q 24089136 1747491 这基本上解决了我的问题 但是 如果您正在设置title from a fragment这无助于options menu 则
  • 如何创建级联下拉列表

    我有两个用于过滤目的的下拉列表 如何将此下拉列表更改为 catchcadaing 下拉列表 public ActionResult Index REFINED DBEntities db new REFINED DBEntities Vie
  • 请帮我解决这个查询(sql server 2008)

    ALTER PROCEDURE ReadNews CategoryID INT Culture TINYINT NULL StartDate DATETIME NULL EndDate DATETIME NULL Start BIGINT
  • Golang - TLS 握手错误

    我正在 go 中运行 https Web 服务器 我正在使用一个有角度的网络应用程序 Chrome 浏览器 来测试它 该应用程序向网络服务器发出 ajax 调用 如果我不断地访问网络服务器 一切似乎都正常 但是每当我让它闲置一段时间并访问网
  • OpenGLES 中缺少的函数的替代列表

    有许多函数存在于 OpenGL 中 但不存在于 OpenGLES 1 1 适用于 iPhone 中 是否有一个列表或资源列出了一些可在 OpenGLES 1 1 中使用的替代函数 例如 gluOrtho2D glPolygonMode gl
  • SQL Server 按每小时日期时间计数进行分组?

    create table Events EventID int identity primary key StartDate datetime not null EndDate datetime not null go insert int
  • 如何在 SVG 元素内触发动画?

    我创建了一个简单的 SVG 动画 通过单击 SVG 元素来触发 我想多次重复使用这个动画元素 并希望避免每次都重复定义 但是 当我在 a 中创建元素时
  • 设置原生 Shadow DOM 元素的样式

    我一直相信您能够访问和覆盖 Shadow DOM 元素的样式 我看到了 html5rocks 上的文章 它定义了您可以使用哪些 webkit 特定选择器 http www html5rocks com en tutorials webcom
  • 当一个线程正在编写另一线程可能同时执行的代码时,如何在 ARM 上同步?

    考虑一个多核 ARM 处理器 一个线程正在修改可能由另一线程同时执行的机器代码块 修改线程执行以下类型的更改 将机器代码块标记为跳过 它将跳转指令放置为代码块的第一条指令 这样无论谁执行它都应该跳过其余指令 跳过整个代码块 标记要执行的机器