Linux __sync_synchronize

2024-03-06 23:18
文章标签 linux sync synchronize

本文主要是介绍Linux __sync_synchronize,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

当我们在做多线程编程的时候,会涉及到一个称为memory order的问题。

例如

int x(0),y(0);
x=4;
y=3;

请问,实际执行的时候,这两条赋值语句谁先执行,谁后执行? 会不会有某个时间点,在某个CPU看来,y比x大?

答案很复杂。本文的目的是从非常实践的角度来考虑这个问题。

首先,它分为两个层面。在编译器看来,x和y是两个没有关联的变量,那么编译器有权利调整这两行代码的执行顺序,只要它乐意。

其次,CPU也有权利这么做。

如果我非要严格要求顺序,那么就应该插入一个memory barrier

int x(0),y(0);
x=4;
在此插入memory barrier指令
y=3;

下面要论述,中间那行怎么写。请耐心看下去,因为大多数人都在瞎整。

gcc的手册中有一节叫做”Built-in functions for atomic memory access”,然后里面列举了这样一个函数:

__sync_synchronize (…)

This builtin issues a full memory barrier.

来,我们写段代码试下:

int main(){__sync_synchronize();return 0;
}

然后用gcc4.2编译,

# gcc -S -c test.c

然后看对应的汇编代码,

main:pushq %rbpmovq %rsp, %rbpmovl $0, %eaxleaveret

嗯?Nothing at all !!! 不信你试一试,我的编译环境是Freebsd 9.0 release, gcc (GCC) 4.2.1 20070831 patched [FreeBSD]。 好,我换个高版本的gcc编译器试一试,gcc46 (FreeBSD Ports Collection) 4.6.3 20120113 (prerelease)

main:pushq %rbpmovq %rsp, %rbpmfencemovl $0, %eaxpopq %rbpret

看,多了一行,mfence。 怎么回事呢?这是gcc之前的一个BUG:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36793 。 2008年被发现,然后修复的。其实它之所以是一个BUG,关键在于gcc的开发者很迷惑,mfence在x86 CPU上到底有没有用?有嘛用? 说到这里,我们得到一个结论:gcc的__sync_synchronize()尽量别用,因为你的代码在低版本的gcc下会有BUG。大部分人用的gcc都比4.4低。从CentOS 6开始,默认的编译器才是gcc 4.4。

那么mfence到底能不能提供我们想要的结果呢? 之前intel的手册一直语焉不详,没说清楚。

最新的手册对mfence的解释是:

“Serializes all store and load operations that occurred prior to the MFENCE instruction in the 
program instruction stream”

并且特别强调,这个指令影响的是data memory子系统,而不是指令执行流。

对于单个CPU来说,

"Reads cannot pass earlier MFENCE instructions”

“Writes cannot pass earlier MFENCE instructions. ”

“MFENCE instructions cannot pass earlier reads or writes”

而对于多个CPU来说,

• Individual processors use the same ordering principles as in a single-processor system.

• Writes by a single processor are observed in the same order by all processors.

• Writes from an individual processor are NOT ordered with respect to the writes from other processors.

• Memory ordering obeys causality (memory ordering respects transitive visibility).

• Any two stores are seen in a consistent order by processors other than those performing the stores

简单点说,对于单个CPU,即便你不用mfence,写入顺序也是保证的。

假如你在C++中, std::string* str=new std::string();

那么不会出现str指针已经被赋值但是它指向的对象还未被初始化好的情况。

另一个有趣的问题是,gcc有一个汇编指令是用来控制内存顺序的,请看这段文档:

Accesses to non-volatile objects are not ordered with respect to volatile accesses. You cannot use a volatile object as a memory barrier to order a sequence of writes to non-volatile memory. For instance:

     int *ptr = something;volatile int vobj;*ptr = something;     vobj = 1; 

Unless *ptr and vobj can be aliased, it is not guaranteed that the write to *ptr occurs by the time the update of vobj happens. If you need this guarantee, you must use a stronger memory barrier such as:

     int *ptr = something;     volatile int vobj;      *ptr = something;     asm volatile ("" : : : "memory");    vobj = 1;

经我测试,asm volatile (“” : : : “memory”);并不生成任何汇编代码。也就是说,这个仅仅是给编译器看的。

为了进一步证实我的观点,请看如下从Intel的Threading Building Blocks函数库中摘取的代码:

#define __TBB_compiler_fence() __asm__ __volatile__(“”: : :”memory”)
#define __TBB_control_consistency_helper() __TBB_compiler_fence()
#define __TBB_acquire_consistency_helper() __TBB_compiler_fence()
#define __TBB_release_consistency_helper() __TBB_compiler_fence()

#ifndef __TBB_full_memory_fence
#define __TBB_full_memory_fence() __asm__ __volatile__(“mfence”: : :”memory”)
#endif

能同时起编译器和硬件内存屏障作用的是__asm__ __volatile__(“mfence”: : :”memory”)。注意:mfence!

另外,我们在intel cpu上用的CAS指令都是带lock前缀的。所以在使用CAS的时候完全不必考虑memory order的问题。

最后推荐一篇文章:《Mathematizing C++ Concurrency》http://www.cl.cam.ac.uk/~pes20/cpp/popl085ap-sewell.pdf 第一作者是剑桥的某在读博士。)

 

This article is from: https://www.sunchangming.com/blog/post/1632.html

这篇关于Linux __sync_synchronize的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/781683

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全

linux安装、更新、卸载anaconda实践

《linux安装、更新、卸载anaconda实践》Anaconda是基于conda的科学计算环境,集成1400+包及依赖,安装需下载脚本、接受协议、设置路径、配置环境变量,更新与卸载通过conda命令... 目录随意找一个目录下载安装脚本检查许可证协议,ENTER就可以安装完毕之后激活anaconda安装更

Linux查询服务器系统版本号的多种方法

《Linux查询服务器系统版本号的多种方法》在Linux系统管理和维护工作中,了解当前操作系统的版本信息是最基础也是最重要的操作之一,系统版本不仅关系到软件兼容性、安全更新策略,还直接影响到故障排查和... 目录一、引言:系统版本查询的重要性二、基础命令解析:cat /etc/Centos-release详