热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

互斥锁定和解锁功能如何防止CPU重新排序?

如何解决《互斥锁定和解锁功能如何防止CPU重新排序?》经验,为你挑选了2个好方法。

据我所知,函数调用充当编译器障碍,但不作为CPU障碍.

本教程说明如下:

获取锁意味着获取语义,而释放锁意味着释放语义!其间的所有内存操作都包含在一个漂亮的小屏障三明治中,防止任何不希望的内存重新排序跨越边界.

我假设上面的引用是关于CPU重新排序而不是编译器重新排序.

但我不明白互斥锁和解锁如何导致CPU赋予这些函数获取和释放语义.

例如,如果我们有以下C代码:

pthread_mutex_lock(&lock);
i = 10;
j = 20;
pthread_mutex_unlock(&lock);

上面的C代码被翻译成以下(伪)汇编指令:

push the address of lock into the stack
call pthread_mutex_lock()
mov 10 into i
mov 20 into j
push the address of lock into the stack
call pthread_mutex_unlock()

现在是什么阻止了CPU重新排序mov 10 into i以及mov 20 into j 上方call pthread_mutex_lock()或下方call pthread_mutex_unlock()

如果它是call阻止CPU进行重新排序的指令,那么为什么我引用的教程使它看起来像是互斥锁和解锁函数来阻止CPU重新排序,为什么我引用的教程没有说任何函数调用会阻止CPU重新排序吗?

我的问题是关于x86架构.



1> Peter Cordes..:

如果ij是局部变量,则什么也没有。如果编译器可以证明当前函数之外的任何内容都没有其地址,则编译器可以在函数调用期间将它们保留在寄存器中。

但是,任何全局变量,或当地人其地址可以被存储在一个全球性的,必须是“同步”在内存中一个非内联函数调用。 编译器必须假定它无法内联的任何函数调用都会修改其可能引用的任何变量。

因此,例如,如果int i;是局部变量,则sscanf("0", "%d", &i);其地址将转义函数后,编译器将不得不在函数调用周围溢出/重新加载它,而不是将其保留在保留调用的寄存器中。

请参阅我的理解volatile asm vs volatile变量的答案,并举一个例子,说明asm volatile("":::"memory")它的地址超出了函数(sscanf("0", "%d", &i);)的局部变量的屏障,而不是仍然纯粹是局部的局部变量。出于完全相同的原因,这是完全相同的行为。


我假设以上引用是在谈论CPU重新排序,而不是在编译器重新排序。

谈论两者,因为两者对于正确性都是必需的。

这就是为什么编译器无法通过任何函数调用对共享变量的更新进行重新排序的原因。(这非常重要:弱的C11内存模型允许大量的编译时重新排序。强的x86内存模型仅允许StoreLoad重新排序和本地存储转发。)

pthread_mutex_lock作为非内联函数调用需要进行编译时重新排序,并且它执行locked操作(原子RMW)这一事实也意味着它在x86上包括完整的运行时内存屏障。(call尽管不是指令本身,而只是函数体内的代码。)这使它获得了语义。

解锁自旋锁仅需要一个释放存储,而不需要RMW,因此根据实现细节,解锁功能可能不是StoreLoad障碍。(这仍然可以:它使关键部分中的所有内容都不会消失。不必在解锁之前停止以后的操作出现。请参阅Jeff Preshing的文章,解释Acquire和Release语义)

在弱序的ISA上,那些互斥函数将运行屏障指令,例如ARM dmb(数据存储器屏障)。普通函数不会,所以该指南的作者正确地指出那些函数是特殊的。


现在是什么阻止CPU将 mov 10重新排序为i并将mov 20重新排序为j到上面call pthread_mutex_lock()

这不是重要的原因(因为在弱排序的ISA上pthread_mutex_unlock会运行屏障指令),但实际上在x86上确实不能使用call指令对存储进行重新排序,更不用说对这些存储进行实际的锁定/解锁了。函数返回之前由函数主体完成的互斥。

x86具有很强的内存排序语义(商店不会与其他商店重新排序),并且call是商店(推送返回地址)。

因此mov [i], 10必须在call指令完成的存储之间出现在全局存储中。

当然,在普通程序中,没有人观察其他线程的调用堆栈,只是观察xchg互斥量或释放存储以释放它pthread_mutex_unlock



2> BeeOnRope..:

简短的回答是是,本体pthread_mutex_lockpthread_mutex_unlock呼叫将包括将在防止CPU移动存储器中的临界区内访问在其外部的必要特定于平台的存储器的障碍.指令流将通过指令从调用代码移动到lockunlock函数中call,为了重新排序,您必须考虑这个动态指令跟踪 - 而不是您在汇编列表中看到的静态序列.

特别是在x86上,你可能在这些方法中找不到明确的独立内存屏障,因为你已经有了lock预先指定的指令,以便以原子方式执行实际的锁定和解锁,这些指令意味着一个完整的内存屏障,这可以防止你担心的CPU重新排序.

例如,在我的带有glibc 2.23的Ubuntu 16.04系统上,pthread_mutex_lock使用lock cmpxchg(比较和交换)pthread_mutex_unlock实现并使用lock dec(递减)实现,两者都具有完全屏障语义.


@ user8426277 - 因为它是_dynamic_指令流(大部分)对重新排序很重要.CPU不会将`call`作为单个指令执行,然后继续执行源/汇编顺序中的下一条指令,它将_into_作为`pthread`函数的主体.因此,为了分析的目的,您可以想象`lock`和`unlock`的整个主体在`call`指令出现的位置调用程序集中的"内联".如果不是这种情况,互斥体和许多其他同步机制将无法实现为普通函数调用!
推荐阅读
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 线程漫谈——线程基础
    本系列意在记录Windwos线程的相关知识点,包括线程基础、线程调度、线程同步、TLS、线程池等。进程与线程理解线程是至关重要的,每个进程至少有一个线程,进程是线程的容器,线程才是真正的执行体,线程必 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 技嘉秀高端B450主板:不再支持第七代APU,性价比高且兼容锐龙一代和二代
    在台北电脑展上,技嘉展示了一款高端的B450主板,型号为“b450 aorus pro wi-fi”。该主板具有10+1相供电、散热片覆盖的供电区域和芯片组,以及两个m.2插槽和背部IO挡板。虽然不支持第七代APU bristol ridge,但它兼容锐龙一代和二代,且具有较高的性价比。该主板还配备了音频声卡、Wi-Fi无线网卡等功能,是一款性能出色且设计精良的主板。 ... [详细]
  • 2016 linux发行版排行_灵越7590 安装 linux (manjarognome)
    RT之前做了一次灵越7590黑苹果炒作业的文章,希望能够分享给更多不想折腾的人。kawauso:教你如何给灵越7590黑苹果抄作业​zhuanlan.z ... [详细]
  • 通过Anaconda安装tensorflow,并安装运行spyder编译器的完整教程
    本文提供了一个完整的教程,介绍了如何通过Anaconda安装tensorflow,并安装运行spyder编译器。文章详细介绍了安装Anaconda、创建tensorflow环境、安装GPU版本tensorflow、安装和运行Spyder编译器以及安装OpenCV等步骤。该教程适用于Windows 8操作系统,并提供了相关的网址供参考。通过本教程,读者可以轻松地安装和配置tensorflow环境,以及运行spyder编译器进行开发。 ... [详细]
  • 如何使用PLEX播放组播、抓取信号源以及设置路由器
    本文介绍了如何使用PLEX播放组播、抓取信号源以及设置路由器。通过使用xTeve软件和M3U源,用户可以在PLEX上实现直播功能,并且可以自动匹配EPG信息和定时录制节目。同时,本文还提供了从华为itv盒子提取组播地址的方法以及如何在ASUS固件路由器上设置IPTV。在使用PLEX之前,建议先使用VLC测试是否可以正常播放UDPXY转发的iptv流。最后,本文还介绍了docker版xTeve的设置方法。 ... [详细]
  • x86 linux的进程调度,x86体系结构下Linux2.6.26的进程调度和切换
    进程调度相关数据结构task_structtask_struct是进程在内核中对应的数据结构,它标识了进程的状态等各项信息。其中有一项thread_struct结构的 ... [详细]
  • 三、查看Linux版本查看系统版本信息的命令:lsb_release-a[root@localhost~]#lsb_release-aLSBVersion::co ... [详细]
  • UNIX高级环境编程 第11、12章 线程及其属性
    第11章线程11.2线程概念线程资源:线程ID,一组寄存器,栈,调度优先级和策略,信号屏蔽字,e ... [详细]
  • Howtobuilda./configure&&make&&makeins ... [详细]
author-avatar
POWER_WALKING_823
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有